diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/atom.c')
| -rw-r--r-- | drivers/gpu/drm/radeon/atom.c | 95 | 
1 files changed, 71 insertions, 24 deletions
| diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d75788feac6..bcec2d79636 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -24,6 +24,7 @@  #include <linux/module.h>  #include <linux/sched.h> +#include <linux/slab.h>  #include <asm/unaligned.h>  #define ATOM_DEBUG @@ -52,15 +53,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 +607,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 +681,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 +710,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,15 +1131,16 @@ 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;  	unsigned char op;  	atom_exec_context ectx; +	int ret = 0;  	if (!base) -		return; +		return -EINVAL;  	len = CU16(base + ATOM_CT_SIZE_PTR);  	ws = CU8(base + ATOM_CT_WS_PTR); @@ -1125,6 +1153,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 +1167,12 @@ 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); +			ret = -EINVAL; +			goto free; +		}  		if (op < ATOM_OP_CNT && op > 0)  			opcode_table[op].func(&ectx, &ptr, @@ -1150,12 +1186,16 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3  	debug_depth--;  	SDEBUG("<<\n"); +free:  	if (ws)  		kfree(ectx.ws); +	return ret;  } -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 +1203,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 +1289,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) @@ -1260,12 +1299,16 @@ void atom_destroy(struct atom_context *ctx)  	kfree(ctx);  } -void atom_parse_data_header(struct atom_context *ctx, int index, +bool atom_parse_data_header(struct atom_context *ctx, int index,  			    uint16_t * size, uint8_t * frev, uint8_t * crev,  			    uint16_t * data_start)  {  	int offset = index * 2 + 4;  	int idx = CU16(ctx->data_table + offset); +	u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4); + +	if (!mdt[index]) +		return false;  	if (size)  		*size = CU16(idx); @@ -1274,38 +1317,42 @@ void atom_parse_data_header(struct atom_context *ctx, int index,  	if (crev)  		*crev = CU8(idx + 3);  	*data_start = idx; -	return; +	return true;  } -void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, +bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,  			   uint8_t * crev)  {  	int offset = index * 2 + 4;  	int idx = CU16(ctx->cmd_table + offset); +	u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4); + +	if (!mct[index]) +		return false;  	if (frev)  		*frev = CU8(idx + 2);  	if (crev)  		*crev = CU8(idx + 3); -	return; +	return true;  }  int atom_allocate_fb_scratch(struct atom_context *ctx)  {  	int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);  	uint16_t data_offset; -	int usage_bytes; +	int usage_bytes = 0;  	struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; -	atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset); +	if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) { +		firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); -	firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); +		DRM_DEBUG("atom firmware requested %08x %dkb\n", +			  firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, +			  firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); -	DRM_DEBUG("atom firmware requested %08x %dkb\n", -		  firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, -		  firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); - -	usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; +		usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; +	}  	if (usage_bytes == 0)  		usage_bytes = 20 * 1024;  	/* allocate some scratch memory */ | 
