[prev in list] [next in list] [prev in thread] [next in thread]
List: freebsd-emulation
Subject: Guest OS PowerOff issue in vbox 'apm' module for 32-bit protected mode interface.
From: Ashutosh Kumar <ashutosh () xinuos ! com>
Date: 2016-09-15 8:01:13
Message-ID: 647512314.1508652.1473926473830.JavaMail.zimbra () xinuos ! com
[Download RAW message or body]
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
_______________________________________________
freebsd-emulation@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-emulation
To unsubscribe, send any mail to "freebsd-emulation-unsubscribe@freebsd.org"
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic