[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