diff options
author | Jerome Glisse <jglisse@redhat.com> | 2010-03-02 20:37:52 +0100 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-03-15 09:35:45 +1000 |
commit | c21b0fe6de3912f53087b4f3991942529f03eef6 (patch) | |
tree | effd4e98793dbd06a85f558a843519b1bf35bf24 /drivers/gpu/drm/radeon/atom.c | |
parent | 338e2b1d571e4873908b199c90d6a31f65137fe3 (diff) |
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>
Diffstat (limited to 'drivers/gpu/drm/radeon/atom.c')
-rw-r--r-- | drivers/gpu/drm/radeon/atom.c | 59 |
1 files changed, 47 insertions, 12 deletions
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d75788feac6c..b7fe660985c4 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -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) |