Guest OS PowerOff issue in vbox 'apm' module for 32-bit protected mode interface.
Ashutosh Kumar
ashutosh at xinuos.com
Thu Sep 15 08:08:23 UTC 2016
Hello,
In order to understand why a particular OS using the BIOS 'apm' module is able to poweroff on VMWare but not on VirtualBox(v4.3.38.1), we analyzed the VirtualBox module at source level. The OS is using 32-bit protected interface of APM to issue power off command to APM module of VirtualBox. Our analysis is that the issue is in VirtualBox’s implementation of APM’s ‘power off’ in 32-bit protected mode. Please see the detailed explanation below:
In 32-bit protected mode when, user issues power off command then the Guest OS calls the function ‘apm_pm32_entry’ of VirtualBox to change the power state to power off. The function ‘apm_pm32_entry’ in VirtualBox then calls function ‘apm_pm16_entry_from_32’ to handle the user call. This function validates the arguments and calls function ‘apm_worker’ to do the actual work:
<snip>
; APM function dispatch table
apm_disp:
dw offset apmf_disconnect ; 04h
dw offset apmf_idle ; 05h
dw offset apmf_busy ; 06h
dw offset apmf_set_state ; 07h
dw offset apmf_enable ; 08h
dw offset apmf_restore ; 09h
dw offset apmf_get_status ; 0Ah
dw offset apmf_get_event ; 0Bh
dw offset apmf_pwr_state ; 0Ch
dw offset apmf_dev_pm ; 0Dh
dw offset apmf_version ; 0Eh
dw offset apmf_engage ; 0Fh
dw offset apmf_get_caps ; 10h
apm_disp_end:
apm_worker proc near
sti ; TODO ?? necessary ??
push ax ; check if function is supported...
xor ah, ah
sub al, 4
mov bp, ax
shl bp, 1
cmp al, (apm_disp_end - apm_disp) / 2
pop ax
mov ah, 53h ; put back APM function
jae apmw_bad_func ; validate function range
jmp apm_disp[bp] ; and dispatch
apmf_disconnect: ; function 04h
jmp apmw_success
apmf_idle: ; function 05h
sti
hlt
jmp apmw_success
apmf_busy: ; function 06h
; jmp apmw_success
apmf_set_state: ; function 07h
; jmp apmw_success
apmf_enable: ; function 08h
jmp apmw_success
apmf_restore: ; function 09h
; jmp apmw_success
apmf_get_status: ; function 0Ah
jmp apmw_bad_func
apmf_get_event: ; function 0Bh
mov ah, 80h
jmp apmw_failure
apmf_pwr_state: ; function 0Ch
apmf_dev_pm: ; function 0Dh
jmp apmw_bad_func
apmf_version: ; function 0Eh
mov ax, 0102h
jmp apmw_success
apmf_engage: ; function 0Fh
; TODO do something?
jmp apmw_success
apmf_get_caps: ; function 10h
mov bl, 0 ; no batteries
mov cx, 0 ; no special caps
jmp apmw_success
apmw_success:
clc ; successful return
ret
apmw_bad_func:
mov ah, 09h ; unrecognized device ID - generic
apmw_failure:
stc ; error for unsupported functions
ret
apm_worker endp
</snip>
The power off event corresponds to function value 0x7. So, from the dispatch table the code jumps to tag ‘apmf_set_state’. As we can see in above code for this tag code simply calls ‘apmw_success’ which returns the call back.
So, for 32-bit protected mode interface and for the power off event VirtualBox is not actually powering off the system but simply returns the calls back as success. Due, to this the OS system does not power off on VirtualBox
We also cross checked the 32-bit protected code on QEMU’s seabios. In seabios the 32-bit protected mode function is ‘entry_apm32’ in file ‘romlayout.S’. This function then calls the function ‘handle_apm’ to handle the APM call:
<snip>
void VISIBLE16 VISIBLE32SEG
handle_apm(struct bregs *regs)
{
debug_enter(regs, DEBUG_HDL_apm);
handle_1553(regs);
}
void
handle_1553(struct bregs *regs)
{
if (! CONFIG_APMBIOS) {
set_code_invalid(regs, RET_EUNSUPPORTED);
return;
}
//debug_stub(regs);
switch (regs->al) {
case 0x00: handle_155300(regs); break;
case 0x01: handle_155301(regs); break;
case 0x02: handle_155302(regs); break;
case 0x03: handle_155303(regs); break;
case 0x04: handle_155304(regs); break;
case 0x05: handle_155305(regs); break;
case 0x06: handle_155306(regs); break;
case 0x07: handle_155307(regs); break;
case 0x08: handle_155308(regs); break;
case 0x0a: handle_15530a(regs); break;
case 0x0b: handle_15530b(regs); break;
case 0x0e: handle_15530e(regs); break;
case 0x0f: handle_15530f(regs); break;
case 0x10: handle_155310(regs); break;
default: handle_1553XX(regs); break;
}
}
// APM Set Power State
static void
handle_155307(struct bregs *regs)
{
if (regs->bx != 1) {
set_success(regs);
return;
}
switch (regs->cx) {
case 1:
dprintf(1, "APM standby request\n");
break;
case 2:
dprintf(1, "APM suspend request\n");
break;
case 3:
apm_shutdown();
break;
}
set_success(regs);
}
void
apm_shutdown(void)
{
u16 pm1a_cnt = GET_GLOBAL(acpi_pm1a_cnt);
if (pm1a_cnt)
outw(0x2000, pm1a_cnt);
irq_disable();
for (;;)
hlt();
}
</snip>
So, QEMU issues an internal ACPI request to power off the system when user issues a power off in 32-bit protected mode.
However, the real mode interface of APM module works fine in VirtualBox. When user issues power off command and since this is not 32-bit protected mode the function ‘apm_pm32_entry’ is not called instead call comes to function ‘apm_function’ with AL set to 0x7 and CX set to 0x3:
<snip>
case APM_SET_PWR:
// @todo: validate device ID
// @todo: validate current connection state
switch (CX) {
case APM_PS_STANDBY:
apm_out_str("Standby", APM_PORT);
break;
case APM_PS_SUSPEND:
apm_out_str("Suspend", APM_PORT);
break;
case APM_PS_OFF:
apm_out_str("Shutdown", APM_PORT); /* Should not return. */
break;
default:
SET_AH(APM_ERR_INVAL_PARAM);
SET_CF();
}
break;
</snip>
In this case the code writes a string “Shutdown” on port APM_PORT i.e 0x8900. This port number is polled by VirtualBox Bochs BIOS. On receiving data on this port the function ‘pcbiosIOPortWrite’ in file ‘DevPcBios.cpp’ is called. It handles it as follows:
<snip>
/*
* Bochs BIOS shutdown request.
*/
if (cb == 1 && Port == 0x8900)
{
static const unsigned char szShutdown[] = "Shutdown";
PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
if (u32 == szShutdown[pThis->iShutdown])
{
pThis->iShutdown++;
if (pThis->iShutdown == 8)
{
pThis->iShutdown = 0;
LogRel(("PcBios: 8900h shutdown request\n"));
return PDMDevHlpVMPowerOff(pDevIns);
}
}
else
pThis->iShutdown = 0;
return VINF_SUCCESS;
}
</snip>
As we can see it issues a power off request if the string received on port is “Shutdown”.
If you can provide your inputs on following points then it will be very helpful to u
1. Have you come across a similar issue and what would be your suggestion on fixing this?
2. We think that the fix should be in VirtualBox and is this something you can do or we can do or we need to raise this issue with VirtualBox. Please suggest?
Regards,
Ashutosh
+91 9899653573
More information about the freebsd-emulation
mailing list