device/oprom/realmode: Add vbe return status support as per VBE spec 3.0
Existing coreboot oprom implementation relies on user selected vesa mode through CONFIG_FRAMEBUFFER_VESA_MODE Kconfig option and expects that all oprom might support user selected vesa mode. Take an example: Enabling AMD external radeon PCIE graphics card on ICLRVP with default vesa mode 0x118. Unable to get valid X and Y resolution after executing vbe_get_mode_info() with 0x4118, return data buffer shows 0x0 resolution. It causes further hang while trying to draw bmpblk image at depthcharge. This patch checks for output register AH in all vbe function (0x3 and 0x4f00/1/2) and die() if returns error. Change-Id: Iacd2ce468e038a14424f029df3a0adec3e5fa15c Signed-off-by: Subrata Banik <subrata.banik@intel.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/33737 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
parent
8950cfb66f
commit
c76bfac088
|
@ -50,11 +50,11 @@ extern unsigned char __realmode_buffer;
|
||||||
/* to have a common register file for interrupt handlers */
|
/* to have a common register file for interrupt handlers */
|
||||||
X86EMU_sysEnv _X86EMU_env;
|
X86EMU_sysEnv _X86EMU_env;
|
||||||
|
|
||||||
void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
unsigned int (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
||||||
u32 esi, u32 edi) asmlinkage;
|
u32 esi, u32 edi) asmlinkage;
|
||||||
|
|
||||||
void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
unsigned int (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
|
||||||
u32 esi, u32 edi) asmlinkage;
|
u32 edx, u32 esi, u32 edi) asmlinkage;
|
||||||
|
|
||||||
static void setup_realmode_code(void)
|
static void setup_realmode_code(void)
|
||||||
{
|
{
|
||||||
|
@ -221,6 +221,46 @@ static int vbe_mode_info_valid(void)
|
||||||
return mode_info_valid;
|
return mode_info_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EAX register is used to indicate the completion status upon return from
|
||||||
|
* VBE function in real mode.
|
||||||
|
*
|
||||||
|
* If the VBE function completed successfully then 0x0 is returned in the AH
|
||||||
|
* register. Otherwise the AH register is set with the nature of the failure:
|
||||||
|
*
|
||||||
|
* AH == 0x00: Function call successful
|
||||||
|
* AH == 0x01: Function call failed
|
||||||
|
* AH == 0x02: Function is not supported in the current HW configuration
|
||||||
|
* AH == 0x03: Function call invalid in current video mode
|
||||||
|
*
|
||||||
|
* Return 0 on success else -1 for failure
|
||||||
|
*/
|
||||||
|
static int vbe_check_for_failure(int ah)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
switch (ah) {
|
||||||
|
case 0x0:
|
||||||
|
status = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
printk(BIOS_DEBUG, "VBE: Function call failed!\n");
|
||||||
|
status = -1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
printk(BIOS_DEBUG, "VBE: Function is not supported!\n");
|
||||||
|
status = -1;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
printk(BIOS_DEBUG, "VBE: Unsupported video mode %x!\n",
|
||||||
|
CONFIG_FRAMEBUFFER_VESA_MODE);
|
||||||
|
status = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
static u8 vbe_get_mode_info(vbe_mode_info_t * mi)
|
static u8 vbe_get_mode_info(vbe_mode_info_t * mi)
|
||||||
{
|
{
|
||||||
printk(BIOS_DEBUG, "VBE: Getting information about VESA mode %04x\n",
|
printk(BIOS_DEBUG, "VBE: Getting information about VESA mode %04x\n",
|
||||||
|
@ -228,8 +268,10 @@ static u8 vbe_get_mode_info(vbe_mode_info_t * mi)
|
||||||
char *buffer = PTR_TO_REAL_MODE(__realmode_buffer);
|
char *buffer = PTR_TO_REAL_MODE(__realmode_buffer);
|
||||||
u16 buffer_seg = (((unsigned long)buffer) >> 4) & 0xff00;
|
u16 buffer_seg = (((unsigned long)buffer) >> 4) & 0xff00;
|
||||||
u16 buffer_adr = ((unsigned long)buffer) & 0xffff;
|
u16 buffer_adr = ((unsigned long)buffer) & 0xffff;
|
||||||
realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000,
|
X86_EAX = realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000,
|
||||||
mi->video_mode, 0x0000, buffer_seg, buffer_adr);
|
mi->video_mode, 0x0000, buffer_seg, buffer_adr);
|
||||||
|
if (vbe_check_for_failure(X86_AH))
|
||||||
|
die("\nError: In %s function\n", __func__);
|
||||||
memcpy(mi->mode_info_block, buffer, sizeof(mi->mode_info_block));
|
memcpy(mi->mode_info_block, buffer, sizeof(mi->mode_info_block));
|
||||||
mode_info_valid = 1;
|
mode_info_valid = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -242,8 +284,10 @@ static u8 vbe_set_mode(vbe_mode_info_t * mi)
|
||||||
mi->video_mode |= (1 << 14);
|
mi->video_mode |= (1 << 14);
|
||||||
// request clearing of framebuffer
|
// request clearing of framebuffer
|
||||||
mi->video_mode &= ~(1 << 15);
|
mi->video_mode &= ~(1 << 15);
|
||||||
realmode_interrupt(0x10, VESA_SET_MODE, mi->video_mode,
|
X86_EAX = realmode_interrupt(0x10, VESA_SET_MODE, mi->video_mode,
|
||||||
0x0000, 0x0000, 0x0000, 0x0000);
|
0x0000, 0x0000, 0x0000, 0x0000);
|
||||||
|
if (vbe_check_for_failure(X86_AH))
|
||||||
|
die("\nError: In %s function\n", __func__);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,8 +330,10 @@ void vbe_set_graphics(void)
|
||||||
void vbe_textmode_console(void)
|
void vbe_textmode_console(void)
|
||||||
{
|
{
|
||||||
delay(2);
|
delay(2);
|
||||||
realmode_interrupt(0x10, 0x0003, 0x0000, 0x0000,
|
X86_EAX = realmode_interrupt(0x10, 0x0003, 0x0000, 0x0000,
|
||||||
0x0000, 0x0000, 0x0000);
|
0x0000, 0x0000, 0x0000);
|
||||||
|
if (vbe_check_for_failure(X86_AH))
|
||||||
|
die("\nError: In %s function\n", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
|
int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
|
||||||
|
|
|
@ -33,11 +33,11 @@ extern unsigned int __idt_handler_size;
|
||||||
extern unsigned char __realmode_code;
|
extern unsigned char __realmode_code;
|
||||||
extern unsigned int __realmode_code_size;
|
extern unsigned int __realmode_code_size;
|
||||||
|
|
||||||
extern void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
extern unsigned int (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx,
|
||||||
u32 esi, u32 edi) asmlinkage;
|
u32 edx, u32 esi, u32 edi) asmlinkage;
|
||||||
|
|
||||||
extern void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
extern unsigned int (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
|
||||||
u32 esi, u32 edi) asmlinkage;
|
u32 edx, u32 esi, u32 edi) asmlinkage;
|
||||||
|
|
||||||
#define FAKE_MEMORY_SIZE (1024*1024) // only 1MB
|
#define FAKE_MEMORY_SIZE (1024*1024) // only 1MB
|
||||||
#define INITIAL_EBDA_SEGMENT 0xF600
|
#define INITIAL_EBDA_SEGMENT 0xF600
|
||||||
|
|
|
@ -43,6 +43,10 @@ __idt_handler_size:
|
||||||
.globl __realmode_code
|
.globl __realmode_code
|
||||||
__realmode_code:
|
__realmode_code:
|
||||||
|
|
||||||
|
/* Realmode function return. */
|
||||||
|
__realmode_ret = RELOCATED(.)
|
||||||
|
.long 0
|
||||||
|
|
||||||
/* Realmode IDT pointer structure. */
|
/* Realmode IDT pointer structure. */
|
||||||
__realmode_idt = RELOCATED(.)
|
__realmode_idt = RELOCATED(.)
|
||||||
.word 1023 /* 16 bit limit */
|
.word 1023 /* 16 bit limit */
|
||||||
|
@ -167,6 +171,13 @@ __lcall_instr = RELOCATED(.)
|
||||||
.word 0x0000, 0x0000
|
.word 0x0000, 0x0000
|
||||||
/* ************************************ */
|
/* ************************************ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here is end of real mode call and time to go back to protected mode.
|
||||||
|
* Before that its better to store current eax into some memory address
|
||||||
|
* so that context persist in protected mode too.
|
||||||
|
*/
|
||||||
|
mov %eax, __realmode_ret
|
||||||
|
|
||||||
/* If we got here, we are just about done.
|
/* If we got here, we are just about done.
|
||||||
* Need to get back to protected mode.
|
* Need to get back to protected mode.
|
||||||
*/
|
*/
|
||||||
|
@ -196,7 +207,8 @@ __lcall_instr = RELOCATED(.)
|
||||||
popa
|
popa
|
||||||
|
|
||||||
/* and exit */
|
/* and exit */
|
||||||
// TODO return AX from OPROM call
|
/* return AX from OPROM call */
|
||||||
|
mov __realmode_ret, %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.globl __realmode_interrupt
|
.globl __realmode_interrupt
|
||||||
|
@ -291,6 +303,13 @@ __realmode_interrupt:
|
||||||
__intXX_instr = RELOCATED(.)
|
__intXX_instr = RELOCATED(.)
|
||||||
.byte 0xcd, 0x00 /* This becomes intXX */
|
.byte 0xcd, 0x00 /* This becomes intXX */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here is end of real mode call and time to go back to protected mode.
|
||||||
|
* Before that its better to store current eax into some memory address
|
||||||
|
* so that context persist in protected mode too.
|
||||||
|
*/
|
||||||
|
mov %eax, __realmode_ret
|
||||||
|
|
||||||
/* Ok, the job is done, now go back to protected mode coreboot */
|
/* Ok, the job is done, now go back to protected mode coreboot */
|
||||||
movl %cr0, %eax
|
movl %cr0, %eax
|
||||||
orl $PE, %eax
|
orl $PE, %eax
|
||||||
|
@ -314,6 +333,8 @@ __intXX_instr = RELOCATED(.)
|
||||||
movl __stack, %esp
|
movl __stack, %esp
|
||||||
popf
|
popf
|
||||||
popa
|
popa
|
||||||
|
/* return AX from OPROM call */
|
||||||
|
mov __realmode_ret, %eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
/* This is the 16-bit interrupt entry point called by the IDT stub code.
|
/* This is the 16-bit interrupt entry point called by the IDT stub code.
|
||||||
|
|
Loading…
Reference in New Issue