Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,34 @@
# Copyright 2025 Advanced Micro Devices, Inc.
#
# 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.
RAS_MGR_FILES = amdgpu_ras_sys.o \
amdgpu_ras_mgr.o \
amdgpu_ras_eeprom_i2c.o \
amdgpu_ras_mp1_v13_0.o \
amdgpu_ras_cmd.o \
amdgpu_virt_ras_cmd.o \
amdgpu_ras_process.o \
amdgpu_ras_nbio_v7_9.o
RAS_MGR = $(addprefix $(AMD_GPU_RAS_PATH)/ras_mgr/, $(RAS_MGR_FILES))
AMD_GPU_RAS_FILES += $(RAS_MGR)
@@ -0,0 +1,287 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include <linux/pci.h>
#include "amdgpu.h"
#include "amdgpu_ras.h"
#include "ras_sys.h"
#include "amdgpu_ras_cmd.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_virt_ras_cmd.h"
/* inject address is 52 bits */
#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52)
#define AMDGPU_RAS_TYPE_RASCORE 0x1
#define AMDGPU_RAS_TYPE_AMDGPU 0x2
#define AMDGPU_RAS_TYPE_VF 0x3
static int amdgpu_ras_trigger_error_prepare(struct ras_core_context *ras_core,
struct ras_cmd_inject_error_req *block_info)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret;
if (block_info->block_id == TA_RAS_BLOCK__XGMI_WAFL) {
if (amdgpu_dpm_set_df_cstate(adev, DF_CSTATE_DISALLOW))
RAS_DEV_WARN(adev, "Failed to disallow df cstate");
ret = amdgpu_dpm_set_pm_policy(adev, PP_PM_POLICY_XGMI_PLPD, XGMI_PLPD_DISALLOW);
if (ret && (ret != -EOPNOTSUPP))
RAS_DEV_WARN(adev, "Failed to disallow XGMI power down");
}
return 0;
}
static int amdgpu_ras_trigger_error_end(struct ras_core_context *ras_core,
struct ras_cmd_inject_error_req *block_info)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret;
if (block_info->block_id == TA_RAS_BLOCK__XGMI_WAFL) {
if (amdgpu_ras_intr_triggered())
return 0;
ret = amdgpu_dpm_set_pm_policy(adev, PP_PM_POLICY_XGMI_PLPD, XGMI_PLPD_DEFAULT);
if (ret && (ret != -EOPNOTSUPP))
RAS_DEV_WARN(adev, "Failed to allow XGMI power down");
if (amdgpu_dpm_set_df_cstate(adev, DF_CSTATE_ALLOW))
RAS_DEV_WARN(adev, "Failed to allow df cstate");
}
return 0;
}
static uint64_t local_addr_to_xgmi_global_addr(struct ras_core_context *ras_core,
uint64_t addr)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct amdgpu_xgmi *xgmi = &adev->gmc.xgmi;
return (addr + xgmi->physical_node_id * xgmi->node_segment_size);
}
static int amdgpu_ras_inject_error(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ras_cmd_inject_error_req *req =
(struct ras_cmd_inject_error_req *)cmd->input_buff_raw;
int ret = RAS_CMD__ERROR_GENERIC;
if (req->block_id == RAS_BLOCK_ID__UMC) {
if (amdgpu_ras_mgr_check_retired_addr(adev, req->address)) {
RAS_DEV_WARN(ras_core->dev,
"RAS WARN: inject: 0x%llx has already been marked as bad!\n",
req->address);
return RAS_CMD__ERROR_ACCESS_DENIED;
}
if ((req->address >= adev->gmc.mc_vram_size &&
adev->gmc.mc_vram_size) ||
(req->address >= RAS_UMC_INJECT_ADDR_LIMIT)) {
RAS_DEV_WARN(adev, "RAS WARN: input address 0x%llx is invalid.",
req->address);
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
}
/* Calculate XGMI relative offset */
if (adev->gmc.xgmi.num_physical_nodes > 1 &&
req->block_id != RAS_BLOCK_ID__GFX) {
req->address = local_addr_to_xgmi_global_addr(ras_core, req->address);
}
}
amdgpu_ras_trigger_error_prepare(ras_core, req);
ret = rascore_handle_cmd(ras_core, cmd, data);
amdgpu_ras_trigger_error_end(ras_core, req);
return ret;
}
static int amdgpu_ras_get_ras_safe_fb_addr_ranges(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ras_cmd_dev_handle *input_data =
(struct ras_cmd_dev_handle *)cmd->input_buff_raw;
struct ras_cmd_ras_safe_fb_address_ranges_rsp *ranges =
(struct ras_cmd_ras_safe_fb_address_ranges_rsp *)cmd->output_buff_raw;
struct amdgpu_mem_partition_info *mem_ranges;
uint32_t i = 0;
if ((cmd->input_size != sizeof(*input_data)) ||
(cmd->output_buf_size < sizeof(*ranges)))
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
mem_ranges = adev->gmc.mem_partitions;
for (i = 0; i < adev->gmc.num_mem_partitions; i++) {
ranges->range[i].start = mem_ranges[i].range.fpfn << AMDGPU_GPU_PAGE_SHIFT;
ranges->range[i].size = mem_ranges[i].size;
ranges->range[i].idx = i;
}
ranges->num_ranges = adev->gmc.num_mem_partitions;
ranges->version = 0;
cmd->output_size = sizeof(struct ras_cmd_ras_safe_fb_address_ranges_rsp);
return RAS_CMD__SUCCESS;
}
static int ras_translate_fb_address(struct ras_core_context *ras_core,
enum ras_fb_addr_type src_type,
enum ras_fb_addr_type dest_type,
union ras_translate_fb_address *src_addr,
union ras_translate_fb_address *dest_addr)
{
uint64_t soc_phy_addr;
int ret = RAS_CMD__SUCCESS;
/* Does not need to be queued as event as this is a SW translation */
switch (src_type) {
case RAS_FB_ADDR_SOC_PHY:
soc_phy_addr = src_addr->soc_phy_addr;
break;
case RAS_FB_ADDR_BANK:
ret = ras_cmd_translate_bank_to_soc_pa(ras_core,
src_addr->bank_addr, &soc_phy_addr);
if (ret)
return RAS_CMD__ERROR_GENERIC;
break;
default:
return RAS_CMD__ERROR_INVALID_CMD;
}
switch (dest_type) {
case RAS_FB_ADDR_SOC_PHY:
dest_addr->soc_phy_addr = soc_phy_addr;
break;
case RAS_FB_ADDR_BANK:
ret = ras_cmd_translate_soc_pa_to_bank(ras_core,
soc_phy_addr, &dest_addr->bank_addr);
if (ret)
return RAS_CMD__ERROR_GENERIC;
break;
default:
return RAS_CMD__ERROR_INVALID_CMD;
}
return ret;
}
static int amdgpu_ras_translate_fb_address(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct ras_cmd_translate_fb_address_req *req_buff =
(struct ras_cmd_translate_fb_address_req *)cmd->input_buff_raw;
struct ras_cmd_translate_fb_address_rsp *rsp_buff =
(struct ras_cmd_translate_fb_address_rsp *)cmd->output_buff_raw;
int ret = RAS_CMD__ERROR_GENERIC;
if ((cmd->input_size != sizeof(struct ras_cmd_translate_fb_address_req)) ||
(cmd->output_buf_size < sizeof(*rsp_buff)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if ((req_buff->src_addr_type >= RAS_FB_ADDR_UNKNOWN) ||
(req_buff->dest_addr_type >= RAS_FB_ADDR_UNKNOWN))
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
ret = ras_translate_fb_address(ras_core, req_buff->src_addr_type,
req_buff->dest_addr_type, &req_buff->trans_addr, &rsp_buff->trans_addr);
if (ret)
return RAS_CMD__ERROR_GENERIC;
rsp_buff->version = 0;
cmd->output_size = sizeof(struct ras_cmd_translate_fb_address_rsp);
return RAS_CMD__SUCCESS;
}
static struct ras_cmd_func_map amdgpu_ras_cmd_maps[] = {
{RAS_CMD__INJECT_ERROR, amdgpu_ras_inject_error},
{RAS_CMD__GET_SAFE_FB_ADDRESS_RANGES, amdgpu_ras_get_ras_safe_fb_addr_ranges},
{RAS_CMD__TRANSLATE_FB_ADDRESS, amdgpu_ras_translate_fb_address},
};
int amdgpu_ras_handle_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd, void *data)
{
struct ras_cmd_func_map *ras_cmd = NULL;
int i, res;
for (i = 0; i < ARRAY_SIZE(amdgpu_ras_cmd_maps); i++) {
if (cmd->cmd_id == amdgpu_ras_cmd_maps[i].cmd_id) {
ras_cmd = &amdgpu_ras_cmd_maps[i];
break;
}
}
if (ras_cmd)
res = ras_cmd->func(ras_core, cmd, NULL);
else
res = RAS_CMD__ERROR_UKNOWN_CMD;
return res;
}
int amdgpu_ras_submit_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ras_core_context *cmd_core = ras_core;
int timeout = 60;
int res;
cmd->cmd_res = RAS_CMD__ERROR_INVALID_CMD;
cmd->output_size = 0;
if (amdgpu_sriov_vf(adev))
return amdgpu_virt_ras_handle_cmd(cmd_core, cmd);
if (!ras_core_is_enabled(cmd_core))
return RAS_CMD__ERROR_ACCESS_DENIED;
while (ras_core_gpu_in_reset(cmd_core)) {
msleep(1000);
if (!timeout--)
return RAS_CMD__ERROR_TIMEOUT;
}
res = amdgpu_ras_handle_cmd(cmd_core, cmd, NULL);
if (res == RAS_CMD__ERROR_UKNOWN_CMD)
res = rascore_handle_cmd(cmd_core, cmd, NULL);
cmd->cmd_res = res;
if (!res && (cmd->output_size > cmd->output_buf_size)) {
RAS_DEV_ERR(cmd_core->dev,
"Output size 0x%x exceeds output buffer size 0x%x!\n",
cmd->output_size, cmd->output_buf_size);
return RAS_CMD__SUCCESS_EXEED_BUFFER;
}
return res;
}
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*/
#ifndef __AMDGPU_RAS_CMD_H__
#define __AMDGPU_RAS_CMD_H__
#include "ras.h"
enum amdgpu_ras_cmd_id {
RAS_CMD__AMDGPU_BEGIN = RAS_CMD_ID_AMDGPU_START,
RAS_CMD__TRANSLATE_MEMORY_FD,
RAS_CMD__AMDGPU_SUPPORTED_MAX = RAS_CMD_ID_AMDGPU_END,
};
struct ras_cmd_translate_memory_fd_req {
struct ras_cmd_dev_handle dev;
uint32_t type;
uint32_t fd;
uint64_t address;
uint32_t reserved[4];
};
struct ras_cmd_translate_memory_fd_rsp {
uint32_t version;
uint32_t padding;
uint64_t start;
uint64_t size;
uint32_t reserved[2];
};
int amdgpu_ras_handle_cmd(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data);
int amdgpu_ras_submit_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd);
#endif
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
*
* 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
* AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#include "amdgpu.h"
#include "amdgpu_atomfirmware.h"
#include "amdgpu_ras_eeprom.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_ras_eeprom_i2c.h"
#include "ras_eeprom.h"
/* These are memory addresses as would be seen by one or more EEPROM
* chips strung on the I2C bus, usually by manipulating pins 1-3 of a
* set of EEPROM devices. They form a continuous memory space.
*
* The I2C device address includes the device type identifier, 1010b,
* which is a reserved value and indicates that this is an I2C EEPROM
* device. It also includes the top 3 bits of the 19 bit EEPROM memory
* address, namely bits 18, 17, and 16. This makes up the 7 bit
* address sent on the I2C bus with bit 0 being the direction bit,
* which is not represented here, and sent by the hardware directly.
*
* For instance,
* 50h = 1010000b => device type identifier 1010b, bits 18:16 = 000b, address 0.
* 54h = 1010100b => --"--, bits 18:16 = 100b, address 40000h.
* 56h = 1010110b => --"--, bits 18:16 = 110b, address 60000h.
* Depending on the size of the I2C EEPROM device(s), bits 18:16 may
* address memory in a device or a device on the I2C bus, depending on
* the status of pins 1-3. See top of amdgpu_eeprom.c.
*
* The RAS table lives either at address 0 or address 40000h of EEPROM.
*/
#define EEPROM_I2C_MADDR_0 0x0
#define EEPROM_I2C_MADDR_4 0x40000
#define MAKE_I2C_ADDR(_aa) ((0xA << 3) | (((_aa) >> 16) & 0xF))
#define to_amdgpu_ras(x) (container_of(x, struct amdgpu_ras, eeprom_control))
#define EEPROM_PAGE_BITS 8
#define EEPROM_PAGE_SIZE (1U << EEPROM_PAGE_BITS)
#define EEPROM_PAGE_MASK (EEPROM_PAGE_SIZE - 1)
#define EEPROM_OFFSET_SIZE 2
static int ras_eeprom_i2c_config(struct ras_core_context *ras_core)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ras_eeprom_control *control = &ras_core->ras_eeprom;
u8 i2c_addr;
if (amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) {
/* The address given by VBIOS is an 8-bit, wire-format
* address, i.e. the most significant byte.
*
* Normalize it to a 19-bit EEPROM address. Remove the
* device type identifier and make it a 7-bit address;
* then make it a 19-bit EEPROM address. See top of
* amdgpu_eeprom.c.
*/
i2c_addr = (i2c_addr & 0x0F) >> 1;
control->i2c_address = ((u32) i2c_addr) << 16;
return 0;
}
switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
case IP_VERSION(13, 0, 5):
case IP_VERSION(13, 0, 6):
case IP_VERSION(13, 0, 10):
case IP_VERSION(13, 0, 12):
case IP_VERSION(13, 0, 14):
control->i2c_address = EEPROM_I2C_MADDR_4;
return 0;
default:
return -ENODATA;
}
return -ENODATA;
}
static int ras_eeprom_i2c_xfer(struct ras_core_context *ras_core, u32 eeprom_addr,
u8 *eeprom_buf, u32 buf_size, bool read)
{
struct i2c_adapter *i2c_adap = ras_core->ras_eeprom.i2c_adapter;
u8 eeprom_offset_buf[EEPROM_OFFSET_SIZE];
struct i2c_msg msgs[] = {
{
.flags = 0,
.len = EEPROM_OFFSET_SIZE,
.buf = eeprom_offset_buf,
},
{
.flags = read ? I2C_M_RD : 0,
},
};
const u8 *p = eeprom_buf;
int r;
u16 len;
for (r = 0; buf_size > 0;
buf_size -= len, eeprom_addr += len, eeprom_buf += len) {
/* Set the EEPROM address we want to write to/read from.
*/
msgs[0].addr = MAKE_I2C_ADDR(eeprom_addr);
msgs[1].addr = msgs[0].addr;
msgs[0].buf[0] = (eeprom_addr >> 8) & 0xff;
msgs[0].buf[1] = eeprom_addr & 0xff;
if (!read) {
/* Write the maximum amount of data, without
* crossing the device's page boundary, as per
* its spec. Partial page writes are allowed,
* starting at any location within the page,
* so long as the page boundary isn't crossed
* over (actually the page pointer rolls
* over).
*
* As per the AT24CM02 EEPROM spec, after
* writing into a page, the I2C driver should
* terminate the transfer, i.e. in
* "i2c_transfer()" below, with a STOP
* condition, so that the self-timed write
* cycle begins. This is implied for the
* "i2c_transfer()" abstraction.
*/
len = min(EEPROM_PAGE_SIZE - (eeprom_addr & EEPROM_PAGE_MASK),
buf_size);
} else {
/* Reading from the EEPROM has no limitation
* on the number of bytes read from the EEPROM
* device--they are simply sequenced out.
* Keep in mind that i2c_msg.len is u16 type.
*/
len = min(U16_MAX, buf_size);
}
msgs[1].len = len;
msgs[1].buf = eeprom_buf;
/* This constitutes a START-STOP transaction.
*/
r = i2c_transfer(i2c_adap, msgs, ARRAY_SIZE(msgs));
if (r != ARRAY_SIZE(msgs))
break;
if (!read) {
/* According to EEPROM specs the length of the
* self-writing cycle, tWR (tW), is 10 ms.
*
* TODO: Use polling on ACK, aka Acknowledge
* Polling, to minimize waiting for the
* internal write cycle to complete, as it is
* usually smaller than tWR (tW).
*/
msleep(10);
}
}
return r < 0 ? r : eeprom_buf - p;
}
const struct ras_eeprom_sys_func amdgpu_ras_eeprom_i2c_sys_func = {
.eeprom_i2c_xfer = ras_eeprom_i2c_xfer,
.update_eeprom_i2c_config = ras_eeprom_i2c_config,
};
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2025 Advanced Micro Devices, Inc.
*
* 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) 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.
*/
#ifndef __AMDGPU_RAS_EEPROM_I2C_H__
#define __AMDGPU_RAS_EEPROM_I2C_H__
#include "ras.h"
extern const struct ras_eeprom_sys_func amdgpu_ras_eeprom_i2c_sys_func;
#endif
@@ -0,0 +1,735 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include "amdgpu.h"
#include "amdgpu_reset.h"
#include "amdgpu_xgmi.h"
#include "ras_sys.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_ras_cmd.h"
#include "amdgpu_virt_ras_cmd.h"
#include "amdgpu_ras_process.h"
#include "amdgpu_ras_eeprom_i2c.h"
#include "amdgpu_ras_mp1_v13_0.h"
#include "amdgpu_ras_nbio_v7_9.h"
#define MAX_SOCKET_NUM_PER_HIVE 8
#define MAX_AID_NUM_PER_SOCKET 4
#define MAX_XCD_NUM_PER_AID 2
/* typical ECC bad page rate is 1 bad page per 100MB VRAM */
#define TYPICAL_ECC_BAD_PAGE_RATE (100ULL * SZ_1M)
#define COUNT_BAD_PAGE_THRESHOLD(size) (((size) >> 21) << 4)
/* Reserve 8 physical dram row for possible retirement.
* In worst cases, it will lose 8 * 2MB memory in vram domain
*/
#define RAS_RESERVED_VRAM_SIZE_DEFAULT (16ULL << 20)
static void ras_mgr_init_event_mgr(struct ras_event_manager *mgr)
{
struct ras_event_state *event_state;
int i;
memset(mgr, 0, sizeof(*mgr));
atomic64_set(&mgr->seqno, 0);
for (i = 0; i < ARRAY_SIZE(mgr->event_state); i++) {
event_state = &mgr->event_state[i];
event_state->last_seqno = RAS_EVENT_INVALID_ID;
atomic64_set(&event_state->count, 0);
}
}
static void amdgpu_ras_mgr_init_event_mgr(struct ras_core_context *ras_core)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct ras_event_manager *event_mgr;
struct amdgpu_hive_info *hive;
hive = amdgpu_get_xgmi_hive(adev);
event_mgr = hive ? &hive->event_mgr : &ras_mgr->ras_event_mgr;
/* init event manager with node 0 on xgmi system */
if (!amdgpu_reset_in_recovery(adev)) {
if (!hive || adev->gmc.xgmi.node_id == 0)
ras_mgr_init_event_mgr(event_mgr);
}
if (hive)
amdgpu_put_xgmi_hive(hive);
}
static int amdgpu_ras_mgr_init_aca_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_aca_config *aca_cfg = &config->aca_cfg;
aca_cfg->socket_num_per_hive = MAX_SOCKET_NUM_PER_HIVE;
aca_cfg->aid_num_per_socket = MAX_AID_NUM_PER_SOCKET;
aca_cfg->xcd_num_per_aid = MAX_XCD_NUM_PER_AID;
return 0;
}
static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_eeprom_config *eeprom_cfg = &config->eeprom_cfg;
eeprom_cfg->eeprom_sys_fn = &amdgpu_ras_eeprom_i2c_sys_func;
eeprom_cfg->eeprom_i2c_adapter = adev->pm.ras_eeprom_i2c_bus;
if (eeprom_cfg->eeprom_i2c_adapter) {
const struct i2c_adapter_quirks *quirks =
((struct i2c_adapter *)eeprom_cfg->eeprom_i2c_adapter)->quirks;
if (quirks) {
eeprom_cfg->max_i2c_read_len = quirks->max_read_len;
eeprom_cfg->max_i2c_write_len = quirks->max_write_len;
}
}
/*
* amdgpu_bad_page_threshold is used to config
* the threshold for the number of bad pages.
* -1: Threshold is set to default value
* Driver will issue a warning message when threshold is reached
* and continue runtime services.
* 0: Disable bad page retirement
* Driver will not retire bad pages
* which is intended for debugging purpose.
* -2: Threshold is determined by a formula
* that assumes 1 bad page per 100M of local memory.
* Driver will continue runtime services when threhold is reached.
* 0 < threshold < max number of bad page records in EEPROM,
* A user-defined threshold is set
* Driver will halt runtime services when this custom threshold is reached.
*/
if (amdgpu_bad_page_threshold == NONSTOP_OVER_THRESHOLD)
eeprom_cfg->eeprom_record_threshold_count =
div64_u64(adev->gmc.mc_vram_size, TYPICAL_ECC_BAD_PAGE_RATE);
else if (amdgpu_bad_page_threshold == WARN_NONSTOP_OVER_THRESHOLD)
eeprom_cfg->eeprom_record_threshold_count =
COUNT_BAD_PAGE_THRESHOLD(RAS_RESERVED_VRAM_SIZE_DEFAULT);
else
eeprom_cfg->eeprom_record_threshold_count = amdgpu_bad_page_threshold;
eeprom_cfg->eeprom_record_threshold_config = amdgpu_bad_page_threshold;
return 0;
}
static int amdgpu_ras_mgr_init_mp1_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_mp1_config *mp1_cfg = &config->mp1_cfg;
int ret = 0;
switch (config->mp1_ip_version) {
case IP_VERSION(13, 0, 6):
case IP_VERSION(13, 0, 14):
case IP_VERSION(13, 0, 12):
mp1_cfg->mp1_sys_fn = &amdgpu_ras_mp1_sys_func_v13_0;
break;
default:
RAS_DEV_ERR(adev,
"The mp1(0x%x) ras config is not right!\n",
config->mp1_ip_version);
ret = -EINVAL;
break;
}
return ret;
}
static int amdgpu_ras_mgr_init_nbio_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_nbio_config *nbio_cfg = &config->nbio_cfg;
int ret = 0;
switch (config->nbio_ip_version) {
case IP_VERSION(7, 9, 0):
case IP_VERSION(7, 9, 1):
nbio_cfg->nbio_sys_fn = &amdgpu_ras_nbio_sys_func_v7_9;
break;
default:
RAS_DEV_ERR(adev,
"The nbio(0x%x) ras config is not right!\n",
config->nbio_ip_version);
ret = -EINVAL;
break;
}
return ret;
}
static int amdgpu_ras_mgr_get_ras_psp_system_status(struct ras_core_context *ras_core,
struct ras_psp_sys_status *status)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ta_context *context = &adev->psp.ras_context.context;
status->initialized = context->initialized;
status->session_id = context->session_id;
status->psp_cmd_mutex = &adev->psp.mutex;
return 0;
}
static int amdgpu_ras_mgr_get_ras_ta_init_param(struct ras_core_context *ras_core,
struct ras_ta_init_param *ras_ta_param)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
uint32_t nps_mode;
if (amdgpu_ras_is_poison_mode_supported(adev))
ras_ta_param->poison_mode_en = 1;
if (!adev->gmc.xgmi.connected_to_cpu && !adev->gmc.is_app_apu)
ras_ta_param->dgpu_mode = 1;
ras_ta_param->xcc_mask = adev->gfx.xcc_mask;
ras_ta_param->channel_dis_num = hweight32(adev->gmc.m_half_use) * 2;
ras_ta_param->active_umc_mask = adev->umc.active_mask;
ras_ta_param->vram_type = (uint8_t)adev->gmc.vram_type;
if (!amdgpu_ras_mgr_get_curr_nps_mode(adev, &nps_mode))
ras_ta_param->nps_mode = nps_mode;
return 0;
}
const struct ras_psp_sys_func amdgpu_ras_psp_sys_func = {
.get_ras_psp_system_status = amdgpu_ras_mgr_get_ras_psp_system_status,
.get_ras_ta_init_param = amdgpu_ras_mgr_get_ras_ta_init_param,
};
static int amdgpu_ras_mgr_init_psp_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_psp_config *psp_cfg = &config->psp_cfg;
psp_cfg->psp_sys_fn = &amdgpu_ras_psp_sys_func;
return 0;
}
static int amdgpu_ras_mgr_init_umc_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_umc_config *umc_cfg = &config->umc_cfg;
umc_cfg->umc_vram_type = adev->gmc.vram_type;
return 0;
}
static struct ras_core_context *amdgpu_ras_mgr_create_ras_core(struct amdgpu_device *adev)
{
struct ras_core_config init_config;
memset(&init_config, 0, sizeof(init_config));
init_config.umc_ip_version = amdgpu_ip_version(adev, UMC_HWIP, 0);
init_config.mp1_ip_version = amdgpu_ip_version(adev, MP1_HWIP, 0);
init_config.gfx_ip_version = amdgpu_ip_version(adev, GC_HWIP, 0);
init_config.nbio_ip_version = amdgpu_ip_version(adev, NBIO_HWIP, 0);
init_config.psp_ip_version = amdgpu_ip_version(adev, MP1_HWIP, 0);
if (init_config.umc_ip_version == IP_VERSION(12, 0, 0) ||
init_config.umc_ip_version == IP_VERSION(12, 5, 0))
init_config.aca_ip_version = IP_VERSION(1, 0, 0);
init_config.sys_fn = &amdgpu_ras_sys_fn;
init_config.ras_eeprom_supported = true;
init_config.poison_supported =
amdgpu_ras_is_poison_mode_supported(adev);
amdgpu_ras_mgr_init_aca_config(adev, &init_config);
amdgpu_ras_mgr_init_eeprom_config(adev, &init_config);
amdgpu_ras_mgr_init_mp1_config(adev, &init_config);
amdgpu_ras_mgr_init_nbio_config(adev, &init_config);
amdgpu_ras_mgr_init_psp_config(adev, &init_config);
amdgpu_ras_mgr_init_umc_config(adev, &init_config);
return ras_core_create(&init_config);
}
static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ras_mgr *ras_mgr;
int ret = 0;
/* Disabled by default */
con->uniras_enabled = false;
if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
adev->debug_enable_ras_aca)
con->uniras_enabled = true;
else
return 0;
ras_mgr = kzalloc_obj(*ras_mgr);
if (!ras_mgr)
return -EINVAL;
con->ras_mgr = ras_mgr;
ras_mgr->adev = adev;
ras_mgr->ras_core = amdgpu_ras_mgr_create_ras_core(adev);
if (!ras_mgr->ras_core) {
RAS_DEV_ERR(adev, "Failed to create ras core!\n");
ret = -EINVAL;
goto err1;
}
ras_mgr->ras_core->dev = adev;
amdgpu_ras_process_init(adev);
ret = ras_core_sw_init(ras_mgr->ras_core);
if (ret) {
RAS_DEV_ERR(adev, "ras_core_sw_init failed! ret:%d\n", ret);
goto err2;
}
amdgpu_ras_mgr_init_event_mgr(ras_mgr->ras_core);
if (amdgpu_sriov_vf(adev)) {
ret = amdgpu_virt_ras_sw_init(adev);
if (ret) {
RAS_DEV_ERR(adev,
"Virt ras sw_init failed! ret:%d\n", ret);
goto err3;
}
}
return 0;
err3:
if (ras_mgr->ras_core)
ras_core_sw_fini(ras_mgr->ras_core);
err2:
amdgpu_ras_process_fini(adev);
if (ras_mgr->ras_core)
ras_core_destroy(ras_mgr->ras_core);
err1:
kfree(ras_mgr);
con->ras_mgr = NULL;
return ret;
}
static int amdgpu_ras_mgr_sw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ras_mgr *ras_mgr = (struct amdgpu_ras_mgr *)con->ras_mgr;
if (!con->uniras_enabled)
return 0;
if (!ras_mgr)
return 0;
if (amdgpu_sriov_vf(adev))
amdgpu_virt_ras_sw_fini(adev);
amdgpu_ras_process_fini(adev);
ras_core_sw_fini(ras_mgr->ras_core);
ras_core_destroy(ras_mgr->ras_core);
ras_mgr->ras_core = NULL;
kfree(con->ras_mgr);
con->ras_mgr = NULL;
return 0;
}
static int amdgpu_ras_mgr_hw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
int ret;
if (!con->uniras_enabled)
return 0;
if (!ras_mgr || !ras_mgr->ras_core)
return -EINVAL;
if (amdgpu_sriov_vf(adev))
ret = amdgpu_virt_ras_hw_init(adev);
else
ret = ras_core_hw_init(ras_mgr->ras_core);
if (ret) {
RAS_DEV_ERR(adev, "Failed to initialize hw_init!, ret:%d\n", ret);
return ret;
}
ras_mgr->ras_is_ready = true;
amdgpu_enable_uniras(adev, true);
RAS_DEV_INFO(adev, "AMDGPU RAS Is Ready.\n");
return 0;
}
static int amdgpu_ras_mgr_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!con->uniras_enabled)
return 0;
if (!ras_mgr || !ras_mgr->ras_core)
return -EINVAL;
if (amdgpu_sriov_vf(adev))
amdgpu_virt_ras_hw_fini(adev);
else
ras_core_hw_fini(ras_mgr->ras_core);
ras_mgr->ras_is_ready = false;
return 0;
}
struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(struct amdgpu_device *adev)
{
if (!adev || !adev->psp.ras_context.ras)
return NULL;
return (struct amdgpu_ras_mgr *)adev->psp.ras_context.ras->ras_mgr;
}
static const struct amd_ip_funcs __maybe_unused ras_v1_0_ip_funcs = {
.name = "ras_v1_0",
.sw_init = amdgpu_ras_mgr_sw_init,
.sw_fini = amdgpu_ras_mgr_sw_fini,
.hw_init = amdgpu_ras_mgr_hw_init,
.hw_fini = amdgpu_ras_mgr_hw_fini,
};
const struct amdgpu_ip_block_version ras_v1_0_ip_block = {
.type = AMD_IP_BLOCK_TYPE_RAS,
.major = 1,
.minor = 0,
.rev = 0,
.funcs = &ras_v1_0_ip_funcs,
};
int amdgpu_enable_uniras(struct amdgpu_device *adev, bool enable)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!ras_mgr || !ras_mgr->ras_core)
return -EPERM;
RAS_DEV_INFO(adev, "Enable amdgpu unified ras!");
return ras_core_set_status(ras_mgr->ras_core, enable);
}
bool amdgpu_uniras_enabled(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (amdgpu_sriov_vf(adev))
return amdgpu_virt_ras_remote_uniras_enabled(adev);
if (!ras_mgr || !ras_mgr->ras_core)
return false;
return ras_core_is_enabled(ras_mgr->ras_core);
}
static bool amdgpu_ras_mgr_is_ready(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (ras_mgr && ras_mgr->ras_core && ras_mgr->ras_is_ready &&
ras_core_is_ready(ras_mgr->ras_core))
return true;
return false;
}
int amdgpu_ras_mgr_handle_fatal_interrupt(struct amdgpu_device *adev, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
return ras_core_handle_nbio_irq(ras_mgr->ras_core, data);
}
uint64_t amdgpu_ras_mgr_gen_ras_event_seqno(struct amdgpu_device *adev,
enum ras_seqno_type seqno_type)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
int ret;
uint64_t seq_no;
if (!amdgpu_ras_mgr_is_ready(adev) ||
(seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX))
return 0;
seq_no = ras_core_gen_seqno(ras_mgr->ras_core, seqno_type);
if ((seqno_type == RAS_SEQNO_TYPE_DE) ||
(seqno_type == RAS_SEQNO_TYPE_POISON_CONSUMPTION)) {
ret = ras_core_put_seqno(ras_mgr->ras_core, seqno_type, seq_no);
if (ret) {
RAS_DEV_WARN(adev, "There are too many ras interrupts!");
return 0;
}
}
return seq_no;
}
int amdgpu_ras_mgr_handle_controller_interrupt(struct amdgpu_device *adev, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct ras_ih_info *ih_info = (struct ras_ih_info *)data;
uint64_t seq_no = 0;
int ret = 0;
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
if (ih_info && (ih_info->block == AMDGPU_RAS_BLOCK__UMC)) {
if (ras_mgr->ras_core->poison_supported) {
seq_no = amdgpu_ras_mgr_gen_ras_event_seqno(adev, RAS_SEQNO_TYPE_DE);
RAS_DEV_INFO(adev,
"{%llu} RAS poison is created, no user action is needed.\n",
seq_no);
}
ret = amdgpu_ras_process_handle_umc_interrupt(adev, ih_info);
} else if (ras_mgr->ras_core->poison_supported) {
ret = amdgpu_ras_process_handle_unexpected_interrupt(adev, ih_info);
} else {
RAS_DEV_WARN(adev,
"No RAS interrupt handler for non-UMC block with poison disabled.\n");
}
return ret;
}
int amdgpu_ras_mgr_dispatch_interrupt(struct amdgpu_device *adev, struct ras_ih_info *ih_info)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
uint64_t seq_no = 0;
int ret = 0;
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
if (!ih_info)
return 0;
if (ih_info->block == RAS_BLOCK_ID__UMC) {
if (ras_mgr->ras_core->poison_supported) {
seq_no = amdgpu_ras_mgr_gen_ras_event_seqno(adev, RAS_SEQNO_TYPE_DE);
RAS_DEV_INFO(adev,
"{%llu} RAS poison is created, no user action is needed.\n",
seq_no);
}
ret = amdgpu_ras_process_handle_umc_interrupt(adev, ih_info);
} else if (ras_mgr->ras_core->poison_supported) {
ret = amdgpu_ras_process_handle_consumption_interrupt(adev, ih_info);
} else {
RAS_DEV_WARN(adev,
"No RAS interrupt handler for non-UMC block with poison disabled.\n");
}
return ret;
}
int amdgpu_ras_mgr_handle_consumer_interrupt(struct amdgpu_device *adev, void *data)
{
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
return amdgpu_ras_process_handle_consumption_interrupt(adev, data);
}
int amdgpu_ras_mgr_update_ras_ecc(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
return ras_core_update_ecc_info(ras_mgr->ras_core);
}
int amdgpu_ras_mgr_reset_gpu(struct amdgpu_device *adev, uint32_t flags)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
con->gpu_reset_flags |= flags;
return amdgpu_ras_reset_gpu(adev);
}
bool amdgpu_ras_mgr_check_eeprom_safety_watermark(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return false;
if (ras_fw_eeprom_supported(ras_mgr->ras_core))
return ras_fw_eeprom_check_safety_watermark(ras_mgr->ras_core);
return ras_eeprom_check_safety_watermark(ras_mgr->ras_core);
}
int amdgpu_ras_mgr_get_curr_nps_mode(struct amdgpu_device *adev,
uint32_t *nps_mode)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
uint32_t mode;
if (!amdgpu_ras_mgr_is_ready(adev))
return -EINVAL;
mode = ras_core_get_curr_nps_mode(ras_mgr->ras_core);
if (!mode || mode > AMDGPU_NPS8_PARTITION_MODE)
return -EINVAL;
*nps_mode = mode;
return 0;
}
bool amdgpu_ras_mgr_check_retired_addr(struct amdgpu_device *adev,
uint64_t addr)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return false;
return ras_umc_check_retired_addr(ras_mgr->ras_core, addr);
}
bool amdgpu_ras_mgr_is_rma(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!ras_mgr || !ras_mgr->ras_core || !ras_mgr->ras_is_ready)
return false;
return ras_core_gpu_is_rma(ras_mgr->ras_core);
}
int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev,
uint32_t cmd_id, void *input, uint32_t input_size,
void *output, uint32_t out_size)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct ras_cmd_ctx *cmd_ctx;
uint32_t ctx_buf_size = PAGE_SIZE;
int ret;
if (!amdgpu_sriov_vf(adev) && !amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
cmd_ctx = kzalloc(ctx_buf_size, GFP_KERNEL);
if (!cmd_ctx)
return -ENOMEM;
cmd_ctx->cmd_id = cmd_id;
memcpy(cmd_ctx->input_buff_raw, input, input_size);
cmd_ctx->input_size = input_size;
cmd_ctx->output_buf_size = ctx_buf_size - sizeof(*cmd_ctx);
ret = amdgpu_ras_submit_cmd(ras_mgr->ras_core, cmd_ctx);
if (!ret && !cmd_ctx->cmd_res && output && (out_size == cmd_ctx->output_size))
memcpy(output, cmd_ctx->output_buff_raw, cmd_ctx->output_size);
kfree(cmd_ctx);
return ret;
}
int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev)
{
if (amdgpu_sriov_vf(adev))
return amdgpu_virt_ras_pre_reset(adev);
if (!amdgpu_ras_mgr_is_ready(adev)) {
RAS_DEV_ERR(adev, "Invalid ras suspend!\n");
return -EPERM;
}
amdgpu_ras_process_pre_reset(adev);
return 0;
}
int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev)
{
if (amdgpu_sriov_vf(adev))
return amdgpu_virt_ras_post_reset(adev);
if (!amdgpu_ras_mgr_is_ready(adev)) {
RAS_DEV_ERR(adev, "Invalid ras resume!\n");
return -EPERM;
}
amdgpu_ras_process_post_reset(adev);
return 0;
}
int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
uint64_t addr, uint64_t *nps_page_addr, uint32_t max_page_count)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!amdgpu_ras_mgr_is_ready(adev))
return -EPERM;
if (!nps_page_addr || !max_page_count)
return -EINVAL;
return ras_core_convert_soc_pa_to_cur_nps_pages(ras_mgr->ras_core,
addr, nps_page_addr, max_page_count);
}
@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (c) 2025 Advanced Micro Devices, Inc.
*
* 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
* AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#ifndef __AMDGPU_RAS_MGR_H__
#define __AMDGPU_RAS_MGR_H__
#include "ras.h"
#include "amdgpu_ras_process.h"
enum ras_ih_type {
RAS_IH_NONE,
RAS_IH_FROM_BLOCK_CONTROLLER,
RAS_IH_FROM_CONSUMER_CLIENT,
RAS_IH_FROM_FATAL_ERROR,
};
struct ras_ih_info {
uint32_t block;
union {
struct amdgpu_iv_entry iv_entry;
struct {
uint16_t pasid;
uint32_t reset;
pasid_notify pasid_fn;
void *data;
};
};
};
struct amdgpu_ras_mgr {
struct amdgpu_device *adev;
struct ras_core_context *ras_core;
struct delayed_work retire_page_dwork;
struct ras_event_manager ras_event_mgr;
void *virt_ras_cmd;
uint64_t last_poison_consumption_seqno;
bool ras_is_ready;
bool is_paused;
struct completion ras_event_done;
};
extern const struct amdgpu_ip_block_version ras_v1_0_ip_block;
struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(
struct amdgpu_device *adev);
int amdgpu_enable_uniras(struct amdgpu_device *adev, bool enable);
bool amdgpu_uniras_enabled(struct amdgpu_device *adev);
int amdgpu_ras_mgr_handle_fatal_interrupt(struct amdgpu_device *adev, void *data);
int amdgpu_ras_mgr_handle_controller_interrupt(struct amdgpu_device *adev, void *data);
int amdgpu_ras_mgr_handle_consumer_interrupt(struct amdgpu_device *adev, void *data);
int amdgpu_ras_mgr_dispatch_interrupt(struct amdgpu_device *adev, struct ras_ih_info *ih_info);
int amdgpu_ras_mgr_update_ras_ecc(struct amdgpu_device *adev);
int amdgpu_ras_mgr_reset_gpu(struct amdgpu_device *adev, uint32_t flags);
uint64_t amdgpu_ras_mgr_gen_ras_event_seqno(struct amdgpu_device *adev,
enum ras_seqno_type seqno_type);
bool amdgpu_ras_mgr_check_eeprom_safety_watermark(struct amdgpu_device *adev);
int amdgpu_ras_mgr_get_curr_nps_mode(struct amdgpu_device *adev, uint32_t *nps_mode);
bool amdgpu_ras_mgr_check_retired_addr(struct amdgpu_device *adev,
uint64_t addr);
bool amdgpu_ras_mgr_is_rma(struct amdgpu_device *adev);
int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev,
uint32_t cmd_id, void *input, uint32_t input_size,
void *output, uint32_t out_size);
int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev);
int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev);
int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
uint64_t addr, uint64_t *nps_page_addr, uint32_t max_page_count);
#endif
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include "amdgpu_smu.h"
#include "amdgpu_reset.h"
#include "amdgpu_ras_mp1_v13_0.h"
#define RAS_MP1_MSG_QueryValidMcaCeCount 0x3A
#define RAS_MP1_MSG_McaBankCeDumpDW 0x3B
static const enum smu_message_type pmfw_eeprom_msgs[] = {
[RAS_SMU_GetRASTableVersion] = SMU_MSG_GetRASTableVersion,
[RAS_SMU_GetBadPageCount] = SMU_MSG_GetBadPageCount,
[RAS_SMU_SetTimestamp] = SMU_MSG_SetTimestamp,
[RAS_SMU_GetTimestamp] = SMU_MSG_GetTimestamp,
[RAS_SMU_GetBadPageIpid] = SMU_MSG_GetBadPageIpid,
[RAS_SMU_EraseRasTable] = SMU_MSG_EraseRasTable,
[RAS_SMU_GetBadPageMcaAddr] = SMU_MSG_GetBadPageMcaAddr,
};
static int mp1_v13_0_get_valid_bank_count(struct ras_core_context *ras_core,
u32 msg, u32 *count)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
u32 smu_msg;
int ret = 0;
if (!count)
return -EINVAL;
smu_msg = (msg == RAS_MP1_MSG_QueryValidMcaCeCount) ?
SMU_MSG_QueryValidMcaCeCount : SMU_MSG_QueryValidMcaCount;
if (down_read_trylock(&adev->reset_domain->sem)) {
ret = amdgpu_smu_ras_send_msg(adev, smu_msg, 0, count);
up_read(&adev->reset_domain->sem);
} else {
ret = -RAS_CORE_GPU_IN_MODE1_RESET;
}
if (ret)
*count = 0;
return ret;
}
static int mp1_v13_0_dump_valid_bank(struct ras_core_context *ras_core,
u32 msg, u32 idx, u32 reg_idx, u64 *val)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
uint32_t data[2] = {0, 0};
uint32_t param;
int ret = 0;
int i, offset;
u32 smu_msg = (msg == RAS_MP1_MSG_McaBankCeDumpDW) ?
SMU_MSG_McaBankCeDumpDW : SMU_MSG_McaBankDumpDW;
if (down_read_trylock(&adev->reset_domain->sem)) {
offset = reg_idx * 8;
for (i = 0; i < ARRAY_SIZE(data); i++) {
param = ((idx & 0xffff) << 16) | ((offset + (i << 2)) & 0xfffc);
ret = amdgpu_smu_ras_send_msg(adev, smu_msg, param, &data[i]);
if (ret) {
RAS_DEV_ERR(adev, "ACA failed to read register[%d], offset:0x%x\n",
reg_idx, offset);
break;
}
}
up_read(&adev->reset_domain->sem);
if (!ret)
*val = (uint64_t)data[1] << 32 | data[0];
} else {
ret = -RAS_CORE_GPU_IN_MODE1_RESET;
}
return ret;
}
static int mp1_v13_0_eeprom_send_msg(struct ras_core_context *ras_core,
enum ras_fw_eeprom_cmd index, uint32_t param, uint32_t *read_arg)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret = 0;
if (down_read_trylock(&adev->reset_domain->sem)) {
ret = amdgpu_smu_ras_send_msg(adev,
pmfw_eeprom_msgs[index], param, read_arg);
up_read(&adev->reset_domain->sem);
} else {
ret = -RAS_CORE_GPU_IN_MODE1_RESET;
}
return ret;
}
static int mp1_v13_0_get_ras_enabled_mask(struct ras_core_context *ras_core,
uint64_t *enabled_mask)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret = 0;
if (down_read_trylock(&adev->reset_domain->sem)) {
if (amdgpu_smu_ras_feature_is_enabled(adev, SMU_FEATURE_HROM_EN_BIT))
*enabled_mask |= RAS_CORE_FW_FEATURE_BIT__RAS_EEPROM;
up_read(&adev->reset_domain->sem);
} else {
ret = -RAS_CORE_GPU_IN_MODE1_RESET;
}
return ret;
}
const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0 = {
.mp1_get_valid_bank_count = mp1_v13_0_get_valid_bank_count,
.mp1_dump_valid_bank = mp1_v13_0_dump_valid_bank,
.mp1_send_eeprom_msg = mp1_v13_0_eeprom_send_msg,
.mp1_get_ras_enabled_mask = mp1_v13_0_get_ras_enabled_mask,
};
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*/
#ifndef __AMDGPU_RAS_MP1_V13_0_H__
#define __AMDGPU_RAS_MP1_V13_0_H__
#include "ras.h"
extern const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0;
#endif
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include "amdgpu_ras_mgr.h"
#include "amdgpu_ras_nbio_v7_9.h"
#include "nbio/nbio_7_9_0_offset.h"
#include "nbio/nbio_7_9_0_sh_mask.h"
#include "ivsrcid/nbio/irqsrcs_nbif_7_4.h"
static int nbio_v7_9_set_ras_controller_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned int type,
enum amdgpu_interrupt_state state)
{
/* Dummy function, there is no initialization operation in driver */
return 0;
}
static int nbio_v7_9_process_ras_controller_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
/* By design, the ih cookie for ras_controller_irq should be written
* to BIFring instead of general iv ring. However, due to known bif ring
* hw bug, it has to be disabled. There is no chance the process function
* will be involked. Just left it as a dummy one.
*/
return 0;
}
static int nbio_v7_9_set_ras_err_event_athub_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned int type,
enum amdgpu_interrupt_state state)
{
/* Dummy function, there is no initialization operation in driver */
return 0;
}
static int nbio_v7_9_process_err_event_athub_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
/* By design, the ih cookie for err_event_athub_irq should be written
* to BIFring instead of general iv ring. However, due to known bif ring
* hw bug, it has to be disabled. There is no chance the process function
* will be involked. Just left it as a dummy one.
*/
return 0;
}
static const struct amdgpu_irq_src_funcs nbio_v7_9_ras_controller_irq_funcs = {
.set = nbio_v7_9_set_ras_controller_irq_state,
.process = nbio_v7_9_process_ras_controller_irq,
};
static const struct amdgpu_irq_src_funcs nbio_v7_9_ras_err_event_athub_irq_funcs = {
.set = nbio_v7_9_set_ras_err_event_athub_irq_state,
.process = nbio_v7_9_process_err_event_athub_irq,
};
static int nbio_v7_9_init_ras_controller_interrupt(struct ras_core_context *ras_core, bool state)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int r;
/* init the irq funcs */
adev->nbio.ras_controller_irq.funcs =
&nbio_v7_9_ras_controller_irq_funcs;
adev->nbio.ras_controller_irq.num_types = 1;
/* register ras controller interrupt */
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_BIF,
NBIF_7_4__SRCID__RAS_CONTROLLER_INTERRUPT,
&adev->nbio.ras_controller_irq);
return r;
}
static int nbio_v7_9_init_ras_err_event_athub_interrupt(struct ras_core_context *ras_core,
bool state)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int r;
/* init the irq funcs */
adev->nbio.ras_err_event_athub_irq.funcs =
&nbio_v7_9_ras_err_event_athub_irq_funcs;
adev->nbio.ras_err_event_athub_irq.num_types = 1;
/* register ras err event athub interrupt */
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_BIF,
NBIF_7_4__SRCID__ERREVENT_ATHUB_INTERRUPT,
&adev->nbio.ras_err_event_athub_irq);
return r;
}
const struct ras_nbio_sys_func amdgpu_ras_nbio_sys_func_v7_9 = {
.set_ras_controller_irq_state = nbio_v7_9_init_ras_controller_interrupt,
.set_ras_err_event_athub_irq_state = nbio_v7_9_init_ras_err_event_athub_interrupt,
};
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __AMDGPU_RAS_NBIO_V7_9_H__
#define __AMDGPU_RAS_NBIO_V7_9_H__
extern const struct ras_nbio_sys_func amdgpu_ras_nbio_sys_func_v7_9;
#endif
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: MIT
/*
* Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
*
* 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
* AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#include "amdgpu.h"
#include "amdgpu_reset.h"
#include "amdgpu_xgmi.h"
#include "ras_sys.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_ras_process.h"
#define RAS_MGR_RETIRE_PAGE_INTERVAL 100
#define RAS_EVENT_PROCESS_TIMEOUT 1200
static void ras_process_retire_page_dwork(struct work_struct *work)
{
struct amdgpu_ras_mgr *ras_mgr =
container_of(work, struct amdgpu_ras_mgr, retire_page_dwork.work);
struct amdgpu_device *adev = ras_mgr->adev;
int ret;
if (amdgpu_ras_is_rma(adev))
return;
/* If gpu reset is ongoing, delay retiring the bad pages */
if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) {
schedule_delayed_work(&ras_mgr->retire_page_dwork,
msecs_to_jiffies(RAS_MGR_RETIRE_PAGE_INTERVAL * 3));
return;
}
ret = ras_umc_handle_bad_pages(ras_mgr->ras_core, NULL);
if (!ret)
schedule_delayed_work(&ras_mgr->retire_page_dwork,
msecs_to_jiffies(RAS_MGR_RETIRE_PAGE_INTERVAL));
}
int amdgpu_ras_process_init(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
ras_mgr->is_paused = false;
init_completion(&ras_mgr->ras_event_done);
INIT_DELAYED_WORK(&ras_mgr->retire_page_dwork, ras_process_retire_page_dwork);
return 0;
}
int amdgpu_ras_process_fini(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
ras_mgr->is_paused = false;
/* Save all cached bad pages to eeprom */
flush_delayed_work(&ras_mgr->retire_page_dwork);
cancel_delayed_work_sync(&ras_mgr->retire_page_dwork);
return 0;
}
int amdgpu_ras_process_handle_umc_interrupt(struct amdgpu_device *adev, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!ras_mgr->ras_core)
return -EINVAL;
return ras_process_add_interrupt_req(ras_mgr->ras_core, NULL, true);
}
int amdgpu_ras_process_handle_unexpected_interrupt(struct amdgpu_device *adev, void *data)
{
amdgpu_ras_set_fed(adev, true);
return amdgpu_ras_mgr_reset_gpu(adev, AMDGPU_RAS_GPU_RESET_MODE1_RESET);
}
int amdgpu_ras_process_handle_consumption_interrupt(struct amdgpu_device *adev, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct ras_ih_info *ih_info = (struct ras_ih_info *)data;
struct ras_event_req req;
uint64_t seqno;
if (!ih_info)
return -EINVAL;
if (amdgpu_sriov_vf(adev)) {
if (adev->virt.ops && adev->virt.ops->ras_poison_handler)
adev->virt.ops->ras_poison_handler(adev, ih_info->block);
else
dev_warn(adev->dev,
"No ras_poison_handler interface in SRIOV for block[%d]!\n",
ih_info->block);
return 0;
}
memset(&req, 0, sizeof(req));
req.block = ih_info->block;
req.data = ih_info->data;
req.pasid = ih_info->pasid;
req.pasid_fn = ih_info->pasid_fn;
req.reset = ih_info->reset;
seqno = ras_core_get_seqno(ras_mgr->ras_core,
RAS_SEQNO_TYPE_POISON_CONSUMPTION, false);
/* When the ACA register cannot be read from FW, the poison
* consumption seqno in the fifo will not pop up, so it is
* necessary to check whether the seqno is the previous seqno.
*/
if (seqno == ras_mgr->last_poison_consumption_seqno) {
/* Pop and discard the previous seqno */
ras_core_get_seqno(ras_mgr->ras_core,
RAS_SEQNO_TYPE_POISON_CONSUMPTION, true);
seqno = ras_core_get_seqno(ras_mgr->ras_core,
RAS_SEQNO_TYPE_POISON_CONSUMPTION, false);
}
ras_mgr->last_poison_consumption_seqno = seqno;
req.seqno = seqno;
return ras_process_add_interrupt_req(ras_mgr->ras_core, &req, false);
}
int amdgpu_ras_process_begin(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (ras_mgr->is_paused)
return -EAGAIN;
reinit_completion(&ras_mgr->ras_event_done);
return 0;
}
int amdgpu_ras_process_end(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
complete(&ras_mgr->ras_event_done);
return 0;
}
int amdgpu_ras_process_pre_reset(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
long rc;
if (!ras_mgr || !ras_mgr->ras_core)
return -EINVAL;
if (!ras_mgr->ras_core->is_initialized)
return -EPERM;
ras_mgr->is_paused = true;
/* Wait for RAS event processing to complete */
rc = wait_for_completion_interruptible_timeout(&ras_mgr->ras_event_done,
msecs_to_jiffies(RAS_EVENT_PROCESS_TIMEOUT));
if (rc <= 0)
RAS_DEV_WARN(adev, "Waiting for ras process to complete %s\n",
rc ? "interrupted" : "timeout");
flush_delayed_work(&ras_mgr->retire_page_dwork);
return 0;
}
int amdgpu_ras_process_post_reset(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
if (!ras_mgr || !ras_mgr->ras_core)
return -EINVAL;
if (!ras_mgr->ras_core->is_initialized)
return -EPERM;
ras_mgr->is_paused = false;
schedule_delayed_work(&ras_mgr->retire_page_dwork, 0);
return 0;
}
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright (c) 2025 Advanced Micro Devices, Inc.
*
* 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
* AUTHORS OR COPYRIGHT HOLDERS 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.
*/
#ifndef __AMDGPU_RAS_PROCESS_H__
#define __AMDGPU_RAS_PROCESS_H__
#include "ras_process.h"
#include "amdgpu_ras_mgr.h"
enum ras_ih_type;
int amdgpu_ras_process_init(struct amdgpu_device *adev);
int amdgpu_ras_process_fini(struct amdgpu_device *adev);
int amdgpu_ras_process_handle_umc_interrupt(struct amdgpu_device *adev,
void *data);
int amdgpu_ras_process_handle_unexpected_interrupt(struct amdgpu_device *adev,
void *data);
int amdgpu_ras_process_handle_consumption_interrupt(struct amdgpu_device *adev,
void *data);
int amdgpu_ras_process_begin(struct amdgpu_device *adev);
int amdgpu_ras_process_end(struct amdgpu_device *adev);
int amdgpu_ras_process_pre_reset(struct amdgpu_device *adev);
int amdgpu_ras_process_post_reset(struct amdgpu_device *adev);
#endif
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include "ras_sys.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_ras.h"
#include "amdgpu_reset.h"
static int amdgpu_ras_sys_detect_fatal_event(struct ras_core_context *ras_core, void *data)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret;
uint64_t seq_no;
ret = amdgpu_ras_global_ras_isr(adev);
if (ret)
return ret;
seq_no = amdgpu_ras_mgr_gen_ras_event_seqno(adev, RAS_SEQNO_TYPE_UE);
RAS_DEV_INFO(adev,
"{%llu} Uncorrectable hardware error(ERREVENT_ATHUB_INTERRUPT) detected!\n",
seq_no);
return amdgpu_ras_process_handle_unexpected_interrupt(adev, data);
}
static int amdgpu_ras_sys_poison_consumption_event(struct ras_core_context *ras_core,
void *data)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct ras_event_req *req = (struct ras_event_req *)data;
pasid_notify pasid_fn;
if (!req)
return -EINVAL;
if (req->pasid_fn) {
pasid_fn = (pasid_notify)req->pasid_fn;
pasid_fn(adev, req->pasid, req->data);
}
return 0;
}
static int amdgpu_ras_sys_gen_seqno(struct ras_core_context *ras_core,
enum ras_seqno_type seqno_type, uint64_t *seqno)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct ras_event_manager *event_mgr;
struct ras_event_state *event_state;
struct amdgpu_hive_info *hive;
enum ras_event_type event_type;
uint64_t seq_no;
if (!ras_mgr || !seqno ||
(seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX))
return -EINVAL;
switch (seqno_type) {
case RAS_SEQNO_TYPE_UE:
event_type = RAS_EVENT_TYPE_FATAL;
break;
case RAS_SEQNO_TYPE_CE:
case RAS_SEQNO_TYPE_DE:
event_type = RAS_EVENT_TYPE_POISON_CREATION;
break;
case RAS_SEQNO_TYPE_POISON_CONSUMPTION:
event_type = RAS_EVENT_TYPE_POISON_CONSUMPTION;
break;
default:
event_type = RAS_EVENT_TYPE_INVALID;
break;
}
hive = amdgpu_get_xgmi_hive(adev);
event_mgr = hive ? &hive->event_mgr : &ras_mgr->ras_event_mgr;
event_state = &event_mgr->event_state[event_type];
if ((event_type == RAS_EVENT_TYPE_FATAL) && amdgpu_ras_in_recovery(adev)) {
seq_no = event_state->last_seqno;
} else {
seq_no = atomic64_inc_return(&event_mgr->seqno);
event_state->last_seqno = seq_no;
atomic64_inc(&event_state->count);
}
amdgpu_put_xgmi_hive(hive);
*seqno = seq_no;
return 0;
}
static int amdgpu_ras_sys_event_notifier(struct ras_core_context *ras_core,
enum ras_notify_event event_id, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(ras_core->dev);
int ret = 0;
switch (event_id) {
case RAS_EVENT_ID__BAD_PAGE_DETECTED:
schedule_delayed_work(&ras_mgr->retire_page_dwork, 0);
break;
case RAS_EVENT_ID__POISON_CONSUMPTION:
amdgpu_ras_sys_poison_consumption_event(ras_core, data);
break;
case RAS_EVENT_ID__RESERVE_BAD_PAGE:
ret = amdgpu_ras_reserve_page(ras_core->dev, *(uint64_t *)data);
break;
case RAS_EVENT_ID__FATAL_ERROR_DETECTED:
ret = amdgpu_ras_sys_detect_fatal_event(ras_core, data);
break;
case RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM:
ret = amdgpu_dpm_send_hbm_bad_pages_num(ras_core->dev, *(uint32_t *)data);
break;
case RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP:
ret = amdgpu_dpm_send_hbm_bad_channel_flag(ras_core->dev, *(uint32_t *)data);
break;
case RAS_EVENT_ID__DEVICE_RMA:
ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_RMA, NULL, NULL);
if (!ras_fw_eeprom_supported(ras_core))
ret = amdgpu_dpm_send_rma_reason(ras_core->dev);
break;
case RAS_EVENT_ID__RESET_GPU:
ret = amdgpu_ras_mgr_reset_gpu(ras_core->dev, *(uint32_t *)data);
break;
case RAS_EVENT_ID__RAS_EVENT_PROC_BEGIN:
ret = amdgpu_ras_process_begin(ras_core->dev);
break;
case RAS_EVENT_ID__RAS_EVENT_PROC_END:
ret = amdgpu_ras_process_end(ras_core->dev);
break;
default:
RAS_DEV_WARN(ras_core->dev, "Invalid ras notify event:%d\n", event_id);
break;
}
return ret;
}
static u64 amdgpu_ras_sys_get_utc_second_timestamp(struct ras_core_context *ras_core)
{
return ktime_get_real_seconds();
}
static int amdgpu_ras_sys_check_gpu_status(struct ras_core_context *ras_core,
uint32_t *status)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
uint32_t gpu_status = 0;
if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev))
gpu_status |= RAS_GPU_STATUS__IN_RESET;
if (amdgpu_sriov_vf(adev))
gpu_status |= RAS_GPU_STATUS__IS_VF;
*status = gpu_status;
return 0;
}
static int amdgpu_ras_sys_get_device_system_info(struct ras_core_context *ras_core,
struct device_system_info *dev_info)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
dev_info->device_id = adev->pdev->device;
dev_info->vendor_id = adev->pdev->vendor;
dev_info->socket_id = adev->smuio.funcs->get_socket_id(adev);
return 0;
}
static int amdgpu_ras_sys_gpu_reset_lock(struct ras_core_context *ras_core,
bool down, bool try)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
int ret = 0;
if (down && try)
ret = down_read_trylock(&adev->reset_domain->sem);
else if (down)
down_read(&adev->reset_domain->sem);
else
up_read(&adev->reset_domain->sem);
return ret;
}
static bool amdgpu_ras_sys_detect_ras_interrupt(struct ras_core_context *ras_core)
{
return !!atomic_read(&amdgpu_ras_in_intr);
}
static int amdgpu_ras_sys_get_gpu_mem(struct ras_core_context *ras_core,
enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
{
struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
struct psp_context *psp = &adev->psp;
struct psp_ring *psp_ring;
struct ta_mem_context *mem_ctx;
if (mem_type == GPU_MEM_TYPE_RAS_PSP_RING) {
psp_ring = &psp->km_ring;
gpu_mem->mem_bo = adev->firmware.rbuf;
gpu_mem->mem_size = psp_ring->ring_size;
gpu_mem->mem_mc_addr = psp_ring->ring_mem_mc_addr;
gpu_mem->mem_cpu_addr = psp_ring->ring_mem;
} else if (mem_type == GPU_MEM_TYPE_RAS_PSP_CMD) {
gpu_mem->mem_bo = psp->cmd_buf_bo;
gpu_mem->mem_size = PSP_CMD_BUFFER_SIZE;
gpu_mem->mem_mc_addr = psp->cmd_buf_mc_addr;
gpu_mem->mem_cpu_addr = psp->cmd_buf_mem;
} else if (mem_type == GPU_MEM_TYPE_RAS_PSP_FENCE) {
gpu_mem->mem_bo = psp->fence_buf_bo;
gpu_mem->mem_size = PSP_FENCE_BUFFER_SIZE;
gpu_mem->mem_mc_addr = psp->fence_buf_mc_addr;
gpu_mem->mem_cpu_addr = psp->fence_buf;
} else if (mem_type == GPU_MEM_TYPE_RAS_TA_FW) {
gpu_mem->mem_bo = psp->fw_pri_bo;
gpu_mem->mem_size = PSP_1_MEG;
gpu_mem->mem_mc_addr = psp->fw_pri_mc_addr;
gpu_mem->mem_cpu_addr = psp->fw_pri_buf;
} else if (mem_type == GPU_MEM_TYPE_RAS_TA_CMD) {
mem_ctx = &psp->ras_context.context.mem_context;
gpu_mem->mem_bo = mem_ctx->shared_bo;
gpu_mem->mem_size = mem_ctx->shared_mem_size;
gpu_mem->mem_mc_addr = mem_ctx->shared_mc_addr;
gpu_mem->mem_cpu_addr = mem_ctx->shared_buf;
} else {
return -EINVAL;
}
if (!gpu_mem->mem_bo || !gpu_mem->mem_size ||
!gpu_mem->mem_mc_addr || !gpu_mem->mem_cpu_addr) {
RAS_DEV_ERR(ras_core->dev, "The ras psp gpu memory is invalid!\n");
return -ENOMEM;
}
return 0;
}
static int amdgpu_ras_sys_put_gpu_mem(struct ras_core_context *ras_core,
enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
{
return 0;
}
const struct ras_sys_func amdgpu_ras_sys_fn = {
.ras_notifier = amdgpu_ras_sys_event_notifier,
.get_utc_second_timestamp = amdgpu_ras_sys_get_utc_second_timestamp,
.gen_seqno = amdgpu_ras_sys_gen_seqno,
.check_gpu_status = amdgpu_ras_sys_check_gpu_status,
.get_device_system_info = amdgpu_ras_sys_get_device_system_info,
.gpu_reset_lock = amdgpu_ras_sys_gpu_reset_lock,
.detect_ras_interrupt = amdgpu_ras_sys_detect_ras_interrupt,
.get_gpu_mem = amdgpu_ras_sys_get_gpu_mem,
.put_gpu_mem = amdgpu_ras_sys_put_gpu_mem,
};
@@ -0,0 +1,605 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#include <linux/pci.h>
#include "amdgpu.h"
#include "amdgpu_ras.h"
#include "ras_sys.h"
#include "amdgpu_ras_cmd.h"
#include "amdgpu_virt_ras_cmd.h"
#include "amdgpu_ras_mgr.h"
static int amdgpu_virt_ras_get_cmd_shared_mem(struct ras_core_context *ras_core,
uint32_t cmd, uint32_t mem_size, struct amdgpu_virt_shared_mem *shared_mem)
{
struct amdgpu_device *adev = ras_core->dev;
struct amdsriov_ras_telemetry *ras_telemetry_cpu;
struct amdsriov_ras_telemetry *ras_telemetry_gpu;
void *fw_va = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].cpu_ptr;
void *drv_va = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].cpu_ptr;
uint64_t fw_vram_usage_start_offset = 0;
uint64_t ras_telemetry_offset = 0;
if (!adev->virt.fw_reserve.ras_telemetry)
return -EINVAL;
if (fw_va && fw_va <= adev->virt.fw_reserve.ras_telemetry) {
fw_vram_usage_start_offset = adev->mman.resv_region[AMDGPU_RESV_FW_VRAM_USAGE].offset;
ras_telemetry_offset = (uintptr_t)adev->virt.fw_reserve.ras_telemetry -
(uintptr_t)fw_va;
} else if (drv_va && drv_va <= adev->virt.fw_reserve.ras_telemetry) {
fw_vram_usage_start_offset = adev->mman.resv_region[AMDGPU_RESV_DRV_VRAM_USAGE].offset;
ras_telemetry_offset = (uintptr_t)adev->virt.fw_reserve.ras_telemetry -
(uintptr_t)drv_va;
} else {
return -EINVAL;
}
ras_telemetry_cpu =
(struct amdsriov_ras_telemetry *)adev->virt.fw_reserve.ras_telemetry;
ras_telemetry_gpu =
(struct amdsriov_ras_telemetry *)(uintptr_t)(fw_vram_usage_start_offset +
ras_telemetry_offset);
if (cmd == RAS_CMD__GET_ALL_BLOCK_ECC_STATUS) {
if (mem_size > AMD_SRIOV_UNIRAS_BLOCKS_BUF_SIZE)
return -ENOMEM;
shared_mem->cpu_addr = ras_telemetry_cpu->uniras_shared_mem.blocks_ecc_buf;
shared_mem->gpa =
(uintptr_t)ras_telemetry_gpu->uniras_shared_mem.blocks_ecc_buf -
adev->gmc.vram_start;
shared_mem->size = mem_size;
} else {
if (mem_size > AMD_SRIOV_UNIRAS_CMD_MAX_SIZE)
return -ENOMEM;
shared_mem->cpu_addr = ras_telemetry_cpu->uniras_shared_mem.cmd_buf;
shared_mem->gpa =
(uintptr_t)ras_telemetry_gpu->uniras_shared_mem.cmd_buf -
adev->gmc.vram_start;
shared_mem->size = mem_size;
}
return 0;
}
static int amdgpu_virt_ras_remote_ioctl_cmd(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *output_data, uint32_t output_size)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(ras_core->dev);
struct amdgpu_virt_ras_cmd *virt_ras = ras_mgr->virt_ras_cmd;
uint32_t mem_len = ALIGN(sizeof(*cmd) + output_size, AMDGPU_GPU_PAGE_SIZE);
struct ras_cmd_ctx *rcmd;
struct ras_cmd_ctx hdr_snap;
struct amdgpu_virt_shared_mem shared_mem = {0};
int ret = 0;
mutex_lock(&virt_ras->remote_access_lock);
ret = amdgpu_virt_ras_get_cmd_shared_mem(ras_core, cmd->cmd_id, mem_len, &shared_mem);
if (ret)
goto out;
rcmd = (struct ras_cmd_ctx *)shared_mem.cpu_addr;
memset(rcmd, 0, mem_len);
memcpy(rcmd, cmd, sizeof(*cmd));
ret = amdgpu_virt_send_remote_ras_cmd(ras_core->dev,
shared_mem.gpa, mem_len);
if (!ret) {
/*
* rcmd lives in shared memory the PF can mutate at any time.
* Snapshot the entire fixed-size response header into a local
* struct in one shot so every subsequent decision (cmd_res,
* output_size, version, etc.) operates on a stable copy. This
* defeats double-fetch / TOCTOU attacks where a malicious or
* buggy PF could flip cmd_res from SUCCESS to an error after
* our success branch, or enlarge output_size between the
* bounds check and the memcpy below to corrupt the caller's
* local output buffer.
*/
memcpy(&hdr_snap, rcmd, sizeof(hdr_snap));
barrier();
if (hdr_snap.cmd_res) {
ret = hdr_snap.cmd_res;
goto out;
}
cmd->cmd_res = hdr_snap.cmd_res;
cmd->output_size = hdr_snap.output_size;
if (hdr_snap.output_size && output_data &&
hdr_snap.output_size <= output_size)
memcpy(output_data, rcmd->output_buff_raw, hdr_snap.output_size);
}
out:
mutex_unlock(&virt_ras->remote_access_lock);
return ret;
}
static int amdgpu_virt_ras_send_remote_cmd(struct ras_core_context *ras_core,
uint32_t cmd_id, void *input_data, uint32_t input_size,
void *output_data, uint32_t output_size)
{
struct ras_cmd_ctx rcmd = {0};
int ret;
if (input_size > RAS_CMD_MAX_IN_SIZE)
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
rcmd.cmd_id = cmd_id;
rcmd.input_size = input_size;
memcpy(rcmd.input_buff_raw, input_data, input_size);
ret = amdgpu_virt_ras_remote_ioctl_cmd(ras_core,
&rcmd, output_data, output_size);
if (!ret) {
if (rcmd.output_size != output_size)
return RAS_CMD__ERROR_GENERIC;
}
return ret;
}
static int amdgpu_virt_ras_get_batch_trace_overview(struct ras_core_context *ras_core,
struct ras_log_batch_overview *overview)
{
struct ras_cmd_batch_trace_snapshot_req req = {0};
struct ras_cmd_batch_trace_snapshot_rsp rsp = {0};
int ret;
ret = amdgpu_virt_ras_send_remote_cmd(ras_core, RAS_CMD__GET_BATCH_TRACE_SNAPSHOT,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret)
return ret;
overview->first_batch_id = rsp.start_batch_id;
overview->last_batch_id = rsp.latest_batch_id;
overview->logged_batch_count = rsp.total_batch_num;
return RAS_CMD__SUCCESS;
}
static int amdgpu_virt_ras_get_cper_snapshot(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(ras_core->dev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
int ret;
if (cmd->input_size != sizeof(struct ras_cmd_cper_snapshot_req))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
ret = amdgpu_virt_ras_send_remote_cmd(ras_core, cmd->cmd_id,
cmd->input_buff_raw, cmd->input_size,
cmd->output_buff_raw, sizeof(struct ras_cmd_cper_snapshot_rsp));
if (ret)
return ret;
memset(&virt_ras->batch_mgr, 0, sizeof(virt_ras->batch_mgr));
amdgpu_virt_ras_get_batch_trace_overview(ras_core,
&virt_ras->batch_mgr.batch_overview);
cmd->output_size = sizeof(struct ras_cmd_cper_snapshot_rsp);
return RAS_CMD__SUCCESS;
}
static bool amdgpu_virt_ras_check_batch_cached(struct ras_cmd_batch_trace_record_rsp *rsp,
uint64_t batch_id)
{
return rsp->real_batch_num &&
rsp->real_batch_num <= RAS_CMD_MAX_BATCH_NUM &&
batch_id >= rsp->start_batch_id &&
(batch_id - rsp->start_batch_id) < rsp->real_batch_num;
}
static int amdgpu_virt_ras_get_batch_records(struct ras_core_context *ras_core, uint64_t batch_id,
struct ras_log_info *trace_arr, uint32_t arr_num,
struct ras_cmd_batch_trace_record_rsp *rsp_cache)
{
struct ras_cmd_batch_trace_record_req req = {
.start_batch_id = batch_id,
.batch_num = RAS_CMD_MAX_BATCH_NUM,
};
struct ras_cmd_batch_trace_record_rsp *rsp = rsp_cache;
struct batch_ras_trace_info *batch;
int ret = 0;
uint32_t i;
uint32_t idx;
if (!amdgpu_virt_ras_check_batch_cached(rsp, batch_id)) {
memset(rsp, 0, sizeof(*rsp));
ret = amdgpu_virt_ras_send_remote_cmd(ras_core, RAS_CMD__GET_BATCH_TRACE_RECORD,
&req, sizeof(req), rsp, sizeof(*rsp));
if (ret)
return -EPIPE;
if (!amdgpu_virt_ras_check_batch_cached(rsp, batch_id)) {
memset(rsp, 0, sizeof(*rsp));
return -EIO;
}
}
idx = (uint32_t)(batch_id - rsp->start_batch_id);
batch = &rsp->batchs[idx];
if (batch_id != batch->batch_id ||
batch->trace_num > MAX_RECORD_PER_BATCH ||
(uint32_t)batch->offset + batch->trace_num > RAS_CMD_MAX_TRACE_NUM) {
memset(rsp, 0, sizeof(*rsp));
return -EIO;
}
for (i = 0; i < batch->trace_num && i < arr_num; i++)
memcpy(&trace_arr[i],
&rsp->records[batch->offset + i], sizeof(*trace_arr));
return i;
}
static int amdgpu_virt_ras_get_cper_records(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(ras_core->dev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
struct ras_cmd_cper_record_req *req =
(struct ras_cmd_cper_record_req *)cmd->input_buff_raw;
struct ras_cmd_cper_record_rsp *rsp =
(struct ras_cmd_cper_record_rsp *)cmd->output_buff_raw;
struct ras_log_batch_overview *overview = &virt_ras->batch_mgr.batch_overview;
struct ras_cmd_batch_trace_record_rsp *rsp_cache = &virt_ras->batch_mgr.batch_trace;
struct ras_log_info *trace;
uint32_t trace_count = MAX_RECORD_PER_BATCH;
uint32_t offset = 0, real_data_len = 0;
uint64_t batch_id;
uint8_t *out_buf;
int ret = 0, i, count;
if (cmd->input_size != sizeof(struct ras_cmd_cper_record_req) ||
(cmd->output_buf_size < sizeof(*rsp)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if (!req->buf_size || !req->buf_ptr || !req->cper_num ||
req->buf_size > RAS_CMD_MAX_CPER_BUF_SZ)
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
trace = kzalloc_objs(*trace, trace_count);
if (!trace)
return RAS_CMD__ERROR_GENERIC;
out_buf = kzalloc(req->buf_size, GFP_KERNEL);
if (!out_buf) {
kfree(trace);
return RAS_CMD__ERROR_GENERIC;
}
memset(out_buf, 0, req->buf_size);
for (i = 0; i < req->cper_num; i++) {
batch_id = req->cper_start_id + i;
if (batch_id >= overview->last_batch_id)
break;
count = amdgpu_virt_ras_get_batch_records(ras_core, batch_id,
trace, trace_count,
rsp_cache);
if (count > 0) {
ret = ras_cper_generate_cper(ras_core, trace, count,
&out_buf[offset], req->buf_size - offset, &real_data_len);
if (ret)
break;
offset += real_data_len;
}
}
if ((ret && (ret != -ENOMEM)) ||
copy_to_user(u64_to_user_ptr(req->buf_ptr), out_buf, offset)) {
kfree(out_buf);
kfree(trace);
return RAS_CMD__ERROR_GENERIC;
}
rsp->real_data_size = offset;
rsp->real_cper_num = i;
rsp->remain_num = (ret == -ENOMEM) ? (req->cper_num - i) : 0;
rsp->version = 0;
cmd->output_size = sizeof(struct ras_cmd_cper_record_rsp);
kfree(out_buf);
kfree(trace);
return RAS_CMD__SUCCESS;
}
static int __fill_get_blocks_ecc_cmd(struct amdgpu_device *adev,
struct vram_blocks_ecc *blks_ecc)
{
struct ras_cmd_ctx *rcmd;
if (!blks_ecc || !blks_ecc->shared_mem.cpu_addr)
return -EINVAL;
rcmd = (struct ras_cmd_ctx *)blks_ecc->shared_mem.cpu_addr;
rcmd->cmd_id = RAS_CMD__GET_ALL_BLOCK_ECC_STATUS;
rcmd->input_size = sizeof(struct ras_cmd_blocks_ecc_req);
rcmd->output_buf_size = blks_ecc->shared_mem.size - sizeof(*rcmd);
return 0;
}
static int __set_cmd_auto_update(struct amdgpu_device *adev,
enum ras_cmd_id cmd_id, uint64_t gpa_addr, uint32_t len, bool reg)
{
struct ras_cmd_auto_update_req req = {0};
struct ras_cmd_auto_update_rsp rsp = {0};
int ret;
req.mode = reg ? 1 : 0;
req.cmd_id = cmd_id;
req.addr = gpa_addr;
req.len = len;
ret = amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__SET_CMD_AUTO_UPDATE,
&req, sizeof(req), &rsp, sizeof(rsp));
return ret;
}
static int amdgpu_virt_ras_get_block_ecc(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
struct amdgpu_device *adev = ras_core->dev;
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
struct vram_blocks_ecc *blks_ecc = &virt_ras->blocks_ecc;
struct ras_cmd_ctx *blks_ecc_cmd_ctx;
struct ras_cmd_blocks_ecc_rsp *blks_ecc_rsp;
struct ras_cmd_block_ecc_info_req *input_data =
(struct ras_cmd_block_ecc_info_req *)cmd->input_buff_raw;
struct ras_cmd_block_ecc_info_rsp *output_data =
(struct ras_cmd_block_ecc_info_rsp *)cmd->output_buff_raw;
int ret = 0;
if (cmd->input_size != sizeof(struct ras_cmd_block_ecc_info_req))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if (input_data->block_id >= MAX_RAS_BLOCK_NUM)
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
if (__fill_get_blocks_ecc_cmd(adev, blks_ecc))
return RAS_CMD__ERROR_GENERIC;
if (!virt_ras->blocks_ecc.auto_update_actived) {
ret = __set_cmd_auto_update(adev, RAS_CMD__GET_ALL_BLOCK_ECC_STATUS,
blks_ecc->shared_mem.gpa,
blks_ecc->shared_mem.size, true);
if (ret)
return ret;
blks_ecc->auto_update_actived = true;
}
blks_ecc_cmd_ctx = blks_ecc->shared_mem.cpu_addr;
blks_ecc_rsp = (struct ras_cmd_blocks_ecc_rsp *)blks_ecc_cmd_ctx->output_buff_raw;
output_data->ce_count = blks_ecc_rsp->blocks[input_data->block_id].ce_count;
output_data->ue_count = blks_ecc_rsp->blocks[input_data->block_id].ue_count;
output_data->de_count = blks_ecc_rsp->blocks[input_data->block_id].de_count;
cmd->output_size = sizeof(struct ras_cmd_block_ecc_info_rsp);
return RAS_CMD__SUCCESS;
}
int amdgpu_virt_ras_check_address_validity(struct amdgpu_device *adev,
uint64_t address, bool *hit)
{
struct ras_cmd_address_check_req req = {0};
struct ras_cmd_address_check_rsp rsp = {0};
int ret = 0;
req.address = address;
ret = amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__CHECK_ADDRESS_VALIDITY,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret)
return RAS_CMD__ERROR_GENERIC;
*hit = rsp.result ? true : false;
return RAS_CMD__SUCCESS;
}
int amdgpu_virt_ras_convert_retired_address(struct amdgpu_device *adev,
uint64_t address, uint64_t *pfn, uint32_t max_pfn_sz)
{
struct ras_cmd_convert_retired_address_req req = {0};
struct ras_cmd_convert_retired_address_rsp rsp = {0};
int ret = 0, i;
int retired_page_count;
if (!pfn || !max_pfn_sz)
return -EINVAL;
req.address = address;
ret = amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__CONVERT_RETIRED_ADDRESS,
&req, sizeof(req), &rsp, sizeof(rsp));
if (ret || rsp.retired_count == 0)
return -EINVAL;
retired_page_count = rsp.retired_count > max_pfn_sz ? max_pfn_sz : rsp.retired_count;
for (i = 0; i < retired_page_count; i++)
pfn[i] = rsp.retired_addr[i] >> AMDGPU_GPU_PAGE_SHIFT;
return retired_page_count;
}
static struct ras_cmd_func_map amdgpu_virt_ras_cmd_maps[] = {
{RAS_CMD__GET_CPER_SNAPSHOT, amdgpu_virt_ras_get_cper_snapshot},
{RAS_CMD__GET_CPER_RECORD, amdgpu_virt_ras_get_cper_records},
{RAS_CMD__GET_BLOCK_ECC_STATUS, amdgpu_virt_ras_get_block_ecc},
};
int amdgpu_virt_ras_handle_cmd(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd)
{
struct ras_cmd_func_map *ras_cmd = NULL;
int i, res;
for (i = 0; i < ARRAY_SIZE(amdgpu_virt_ras_cmd_maps); i++) {
if (cmd->cmd_id == amdgpu_virt_ras_cmd_maps[i].cmd_id) {
ras_cmd = &amdgpu_virt_ras_cmd_maps[i];
break;
}
}
if (ras_cmd)
res = ras_cmd->func(ras_core, cmd, NULL);
else
res = amdgpu_virt_ras_remote_ioctl_cmd(ras_core, cmd,
cmd->output_buff_raw, cmd->output_buf_size);
cmd->cmd_res = res;
if (!res && (cmd->output_size > cmd->output_buf_size)) {
RAS_DEV_ERR(ras_core->dev,
"Output data size 0x%x exceeds buffer size 0x%x!\n",
cmd->output_size, cmd->output_buf_size);
return RAS_CMD__SUCCESS_EXEED_BUFFER;
}
return RAS_CMD__SUCCESS;
}
int amdgpu_virt_ras_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras_cmd;
ras_mgr->virt_ras_cmd = kzalloc_obj(struct amdgpu_virt_ras_cmd);
if (!ras_mgr->virt_ras_cmd)
return -ENOMEM;
virt_ras_cmd = ras_mgr->virt_ras_cmd;
mutex_init(&virt_ras_cmd->remote_access_lock);
return 0;
}
int amdgpu_virt_ras_sw_fini(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras_cmd = ras_mgr->virt_ras_cmd;
mutex_destroy(&virt_ras_cmd->remote_access_lock);
kfree(ras_mgr->virt_ras_cmd);
ras_mgr->virt_ras_cmd = NULL;
return 0;
}
int amdgpu_virt_ras_hw_init(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
struct vram_blocks_ecc *blks_ecc = &virt_ras->blocks_ecc;
amdgpu_virt_get_ras_capability(adev);
memset(blks_ecc, 0, sizeof(*blks_ecc));
if (amdgpu_virt_ras_get_cmd_shared_mem(ras_mgr->ras_core,
RAS_CMD__GET_ALL_BLOCK_ECC_STATUS,
AMD_SRIOV_UNIRAS_BLOCKS_BUF_SIZE, &blks_ecc->shared_mem))
return -ENOMEM;
return 0;
}
int amdgpu_virt_ras_hw_fini(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
struct vram_blocks_ecc *blks_ecc = &virt_ras->blocks_ecc;
if (blks_ecc->shared_mem.cpu_addr)
memset(blks_ecc->shared_mem.cpu_addr, 0, blks_ecc->shared_mem.size);
memset(blks_ecc, 0, sizeof(*blks_ecc));
return 0;
}
int amdgpu_virt_ras_pre_reset(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras =
(struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
virt_ras->blocks_ecc.auto_update_actived = false;
return 0;
}
int amdgpu_virt_ras_post_reset(struct amdgpu_device *adev)
{
return 0;
}
void amdgpu_virt_ras_set_remote_uniras(struct amdgpu_device *adev, bool en)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras;
if (!ras_mgr || !ras_mgr->virt_ras_cmd)
return;
virt_ras = (struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
virt_ras->remote_uniras_supported = en;
}
bool amdgpu_virt_ras_remote_uniras_enabled(struct amdgpu_device *adev)
{
struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
struct amdgpu_virt_ras_cmd *virt_ras;
if (amdgpu_in_reset(adev))
return false;
if (!ras_mgr || !ras_mgr->virt_ras_cmd)
return false;
virt_ras = (struct amdgpu_virt_ras_cmd *)ras_mgr->virt_ras_cmd;
return virt_ras->remote_uniras_supported;
}
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*/
#ifndef __AMDGPU_VIRT_RAS_CMD_H__
#define __AMDGPU_VIRT_RAS_CMD_H__
#include "ras.h"
struct remote_batch_trace_mgr {
struct ras_log_batch_overview batch_overview;
struct ras_cmd_batch_trace_record_rsp batch_trace;
};
struct amdgpu_virt_shared_mem {
uint64_t gpa;
void *cpu_addr;
uint32_t size;
};
struct vram_blocks_ecc {
struct amdgpu_virt_shared_mem shared_mem;
bool auto_update_actived;
};
struct amdgpu_virt_ras_cmd {
bool remote_uniras_supported;
struct remote_batch_trace_mgr batch_mgr;
struct vram_blocks_ecc blocks_ecc;
struct mutex remote_access_lock;
};
int amdgpu_virt_ras_sw_init(struct amdgpu_device *adev);
int amdgpu_virt_ras_sw_fini(struct amdgpu_device *adev);
int amdgpu_virt_ras_hw_init(struct amdgpu_device *adev);
int amdgpu_virt_ras_hw_fini(struct amdgpu_device *adev);
int amdgpu_virt_ras_handle_cmd(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd);
int amdgpu_virt_ras_pre_reset(struct amdgpu_device *adev);
int amdgpu_virt_ras_post_reset(struct amdgpu_device *adev);
void amdgpu_virt_ras_set_remote_uniras(struct amdgpu_device *adev, bool en);
bool amdgpu_virt_ras_remote_uniras_enabled(struct amdgpu_device *adev);
int amdgpu_virt_ras_check_address_validity(struct amdgpu_device *adev,
uint64_t address, bool *hit);
int amdgpu_virt_ras_convert_retired_address(struct amdgpu_device *adev,
uint64_t address, uint64_t *pfn, uint32_t max_pfn_sz);
#endif
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __RAS_SYS_H__
#define __RAS_SYS_H__
#include <linux/stdarg.h>
#include <linux/printk.h>
#include <linux/dev_printk.h>
#include <linux/mempool.h>
#include "amdgpu.h"
#define RAS_DEV_ERR(device, fmt, ...) \
do { \
if (device) \
dev_err(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
else \
printk(KERN_ERR fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_WARN(device, fmt, ...) \
do { \
if (device) \
dev_warn(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
else \
printk(KERN_WARNING fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_WARN_RATELIMITED(device, fmt, ...) \
do { \
if (device) \
dev_warn_ratelimited(((struct amdgpu_device *)device)->dev, \
fmt, ##__VA_ARGS__); \
else \
printk_ratelimited(KERN_WARNING fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_INFO(device, fmt, ...) \
do { \
if (device) \
dev_info(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
else \
printk(KERN_INFO fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_DBG(device, fmt, ...) \
do { \
if (device) \
dev_dbg(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
else \
printk(KERN_DEBUG fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
#define RAS_DEV_RREG32_SOC15(dev, ip, inst, reg) \
({ \
struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
__RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg, \
0, ip##_HWIP, inst); \
})
#define RAS_DEV_WREG32_SOC15(dev, ip, inst, reg, value) \
({ \
struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
__WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg), \
value, 0, ip##_HWIP, inst); \
})
/* GET_INST returns the physical instance corresponding to a logical instance */
#define RAS_GET_INST(dev, ip, inst) \
({ \
struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
adev->ip_map.logical_to_dev_inst ? \
adev->ip_map.logical_to_dev_inst(adev, ip##_HWIP, inst) : inst; \
})
#define RAS_GET_MASK(dev, ip, mask) \
({ \
struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
(adev->ip_map.logical_to_dev_mask ? \
adev->ip_map.logical_to_dev_mask(adev, ip##_HWIP, mask) : mask); \
})
static inline void *ras_radix_tree_delete_iter(struct radix_tree_root *root, void *iter)
{
return radix_tree_delete(root, ((struct radix_tree_iter *)iter)->index);
}
static inline long ras_wait_event_interruptible_timeout(void *wq_head,
int (*condition)(void *param), void *param, unsigned int timeout)
{
return wait_event_interruptible_timeout(*(wait_queue_head_t *)wq_head,
condition(param), timeout);
}
extern const struct ras_sys_func amdgpu_ras_sys_fn;
#endif