drm/radeon/kms: catch atombios infinite loop and break out of it
In somecase the atombios code might lead to infinite loop because the GPU is in broken state, this patch track the jump history and will abort atombios execution if we are stuck executing the same jump for more than 1sec. Note that otherwise in some case we might enter an infinite loop in the kernel context which is bad. Signed-off-by: Jerome Glisse <jglisse@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
parent
338e2b1d57
commit
c21b0fe6de
|
@ -52,15 +52,17 @@
|
|||
|
||||
typedef struct {
|
||||
struct atom_context *ctx;
|
||||
|
||||
uint32_t *ps, *ws;
|
||||
int ps_shift;
|
||||
uint16_t start;
|
||||
unsigned last_jump;
|
||||
unsigned long last_jump_jiffies;
|
||||
bool abort;
|
||||
} atom_exec_context;
|
||||
|
||||
int atom_debug = 0;
|
||||
static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
|
||||
void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params);
|
||||
static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
|
||||
int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params);
|
||||
|
||||
static uint32_t atom_arg_mask[8] =
|
||||
{ 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
|
||||
|
@ -604,12 +606,17 @@ static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
|
|||
static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
|
||||
{
|
||||
int idx = U8((*ptr)++);
|
||||
int r = 0;
|
||||
|
||||
if (idx < ATOM_TABLE_NAMES_CNT)
|
||||
SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]);
|
||||
else
|
||||
SDEBUG(" table: %d\n", idx);
|
||||
if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
|
||||
atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
|
||||
r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
|
||||
if (r) {
|
||||
ctx->abort = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
|
||||
|
@ -673,6 +680,8 @@ static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
|
|||
static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
|
||||
{
|
||||
int execute = 0, target = U16(*ptr);
|
||||
unsigned long cjiffies;
|
||||
|
||||
(*ptr) += 2;
|
||||
switch (arg) {
|
||||
case ATOM_COND_ABOVE:
|
||||
|
@ -700,8 +709,25 @@ static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
|
|||
if (arg != ATOM_COND_ALWAYS)
|
||||
SDEBUG(" taken: %s\n", execute ? "yes" : "no");
|
||||
SDEBUG(" target: 0x%04X\n", target);
|
||||
if (execute)
|
||||
if (execute) {
|
||||
if (ctx->last_jump == (ctx->start + target)) {
|
||||
cjiffies = jiffies;
|
||||
if (time_after(cjiffies, ctx->last_jump_jiffies)) {
|
||||
cjiffies -= ctx->last_jump_jiffies;
|
||||
if ((jiffies_to_msecs(cjiffies) > 1000)) {
|
||||
DRM_ERROR("atombios stuck in loop for more than 1sec aborting\n");
|
||||
ctx->abort = true;
|
||||
}
|
||||
} else {
|
||||
/* jiffies wrap around we will just wait a little longer */
|
||||
ctx->last_jump_jiffies = jiffies;
|
||||
}
|
||||
} else {
|
||||
ctx->last_jump = ctx->start + target;
|
||||
ctx->last_jump_jiffies = jiffies;
|
||||
}
|
||||
*ptr = ctx->start + target;
|
||||
}
|
||||
}
|
||||
|
||||
static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
|
||||
|
@ -1104,7 +1130,7 @@ static struct {
|
|||
atom_op_shr, ATOM_ARG_MC}, {
|
||||
atom_op_debug, 0},};
|
||||
|
||||
static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
|
||||
static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
|
||||
{
|
||||
int base = CU16(ctx->cmd_table + 4 + 2 * index);
|
||||
int len, ws, ps, ptr;
|
||||
|
@ -1112,7 +1138,7 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3
|
|||
atom_exec_context ectx;
|
||||
|
||||
if (!base)
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
len = CU16(base + ATOM_CT_SIZE_PTR);
|
||||
ws = CU8(base + ATOM_CT_WS_PTR);
|
||||
|
@ -1125,6 +1151,8 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3
|
|||
ectx.ps_shift = ps / 4;
|
||||
ectx.start = base;
|
||||
ectx.ps = params;
|
||||
ectx.abort = false;
|
||||
ectx.last_jump = 0;
|
||||
if (ws)
|
||||
ectx.ws = kzalloc(4 * ws, GFP_KERNEL);
|
||||
else
|
||||
|
@ -1137,6 +1165,11 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3
|
|||
SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
|
||||
else
|
||||
SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1);
|
||||
if (ectx.abort) {
|
||||
DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
|
||||
base, len, ws, ps, ptr - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (op < ATOM_OP_CNT && op > 0)
|
||||
opcode_table[op].func(&ectx, &ptr,
|
||||
|
@ -1152,10 +1185,13 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3
|
|||
|
||||
if (ws)
|
||||
kfree(ectx.ws);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
||||
int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
||||
{
|
||||
int r;
|
||||
|
||||
mutex_lock(&ctx->mutex);
|
||||
/* reset reg block */
|
||||
ctx->reg_block = 0;
|
||||
|
@ -1163,8 +1199,9 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
|
|||
ctx->fb_base = 0;
|
||||
/* reset io mode */
|
||||
ctx->io_mode = ATOM_IO_MM;
|
||||
atom_execute_table_locked(ctx, index, params);
|
||||
r = atom_execute_table_locked(ctx, index, params);
|
||||
mutex_unlock(&ctx->mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
|
||||
|
@ -1248,9 +1285,7 @@ int atom_asic_init(struct atom_context *ctx)
|
|||
|
||||
if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
|
||||
return 1;
|
||||
atom_execute_table(ctx, ATOM_CMD_INIT, ps);
|
||||
|
||||
return 0;
|
||||
return atom_execute_table(ctx, ATOM_CMD_INIT, ps);
|
||||
}
|
||||
|
||||
void atom_destroy(struct atom_context *ctx)
|
||||
|
|
|
@ -140,7 +140,7 @@ struct atom_context {
|
|||
extern int atom_debug;
|
||||
|
||||
struct atom_context *atom_parse(struct card_info *, void *);
|
||||
void atom_execute_table(struct atom_context *, int, uint32_t *);
|
||||
int atom_execute_table(struct atom_context *, int, uint32_t *);
|
||||
int atom_asic_init(struct atom_context *);
|
||||
void atom_destroy(struct atom_context *);
|
||||
void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start);
|
||||
|
|
Loading…
Reference in New Issue