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-unsubscr...@freebsd.org"

Reply via email to