Date: Thu, 15 Sep 2016 04:01:13 -0400 (EDT) From: Ashutosh Kumar <ashutosh@xinuos.com> To: vbox@FreeBSD.org Cc: Michael Brinke-Engel <mikebren@xinuos.com>, Tim Rice <tim@xinuos.com>, ashutosh.kumar@techmahindra.com Subject: Guest OS PowerOff issue in vbox 'apm' module for 32-bit protected mode interface. Message-ID: <647512314.1508652.1473926473830.JavaMail.zimbra@xinuos.com> In-Reply-To: <1830242058.1508588.1473926236360.JavaMail.zimbra@xinuos.com>
next in thread | previous in thread | raw e-mail | index | archive | help
Hello,=20 In order to understand why a particular OS using the BIOS 'apm' module is a= ble 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 interf= ace of APM to issue power off command to APM module of VirtualBox. Our anal= ysis is that the issue is in VirtualBox=E2=80=99s implementation of APM=E2= =80=99s =E2=80=98power off=E2=80=99 in 32-bit protected mode. Please see th= e detailed explanation below:=20 In 32-bit protected mode when, user issues power off command then the Guest= OS calls the function =E2=80=98apm_pm32_entry=E2=80=99 of VirtualBox to ch= ange the power state to power off. The function =E2=80=98apm_pm32_entry=E2= =80=99 in VirtualBox then calls function =E2=80=98apm_pm16_entry_from_32=E2= =80=99 to handle the user call. This function validates the arguments and c= alls function =E2=80=98apm_worker=E2=80=99 to do the actual work:=20 <snip>=20 ; APM function dispatch table=20 apm_disp:=20 dw offset apmf_disconnect ; 04h=20 dw offset apmf_idle ; 05h=20 dw offset apmf_busy ; 06h=20 dw offset apmf_set_state ; 07h=20 dw offset apmf_enable ; 08h=20 dw offset apmf_restore ; 09h=20 dw offset apmf_get_status ; 0Ah=20 dw offset apmf_get_event ; 0Bh=20 dw offset apmf_pwr_state ; 0Ch=20 dw offset apmf_dev_pm ; 0Dh=20 dw offset apmf_version ; 0Eh=20 dw offset apmf_engage ; 0Fh=20 dw offset apmf_get_caps ; 10h=20 apm_disp_end:=20 apm_worker proc near=20 sti ; TODO ?? necessary ??=20 push ax ; check if function is supported...=20 xor ah, ah=20 sub al, 4=20 mov bp, ax=20 shl bp, 1=20 cmp al, (apm_disp_end - apm_disp) / 2=20 pop ax=20 mov ah, 53h ; put back APM function=20 jae apmw_bad_func ; validate function range=20 jmp apm_disp[bp] ; and dispatch=20 apmf_disconnect: ; function 04h=20 jmp apmw_success=20 apmf_idle: ; function 05h=20 sti=20 hlt=20 jmp apmw_success=20 apmf_busy: ; function 06h=20 ; jmp apmw_success=20 apmf_set_state: ; function 07h=20 ; jmp apmw_success=20 apmf_enable: ; function 08h=20 jmp apmw_success=20 apmf_restore: ; function 09h=20 ; jmp apmw_success=20 apmf_get_status: ; function 0Ah=20 jmp apmw_bad_func=20 apmf_get_event: ; function 0Bh=20 mov ah, 80h=20 jmp apmw_failure=20 apmf_pwr_state: ; function 0Ch=20 apmf_dev_pm: ; function 0Dh=20 jmp apmw_bad_func=20 apmf_version: ; function 0Eh=20 mov ax, 0102h=20 jmp apmw_success=20 apmf_engage: ; function 0Fh=20 ; TODO do something?=20 jmp apmw_success=20 apmf_get_caps: ; function 10h=20 mov bl, 0 ; no batteries=20 mov cx, 0 ; no special caps=20 jmp apmw_success=20 apmw_success:=20 clc ; successful return=20 ret=20 apmw_bad_func:=20 mov ah, 09h ; unrecognized device ID - generic=20 apmw_failure:=20 stc ; error for unsupported functions=20 ret=20 apm_worker endp=20 </snip>=20 The power off event corresponds to function value 0x7. So, from the dispatc= h table the code jumps to tag =E2=80=98apmf_set_state=E2=80=99. As we can s= ee in above code for this tag code simply calls =E2=80=98apmw_success=E2=80= =99 which returns the call back.=20 So, for 32-bit protected mode interface and for the power off event Virtual= Box is not actually powering off the system but simply returns the calls ba= ck as success. Due, to this the OS system does not power off on VirtualBox= =20 We also cross checked the 32-bit protected code on QEMU=E2=80=99s seabios. = In seabios the 32-bit protected mode function is =E2=80=98entry_apm32=E2=80= =99 in file =E2=80=98romlayout.S=E2=80=99. This function then calls the fun= ction =E2=80=98handle_apm=E2=80=99 to handle the APM call:=20 <snip>=20 void VISIBLE16 VISIBLE32SEG=20 handle_apm(struct bregs *regs)=20 {=20 debug_enter(regs, DEBUG_HDL_apm);=20 handle_1553(regs);=20 }=20 void=20 handle_1553(struct bregs *regs)=20 {=20 if (! CONFIG_APMBIOS) {=20 set_code_invalid(regs, RET_EUNSUPPORTED);=20 return;=20 }=20 //debug_stub(regs);=20 switch (regs->al) {=20 case 0x00: handle_155300(regs); break;=20 case 0x01: handle_155301(regs); break;=20 case 0x02: handle_155302(regs); break;=20 case 0x03: handle_155303(regs); break;=20 case 0x04: handle_155304(regs); break;=20 case 0x05: handle_155305(regs); break;=20 case 0x06: handle_155306(regs); break;=20 case 0x07: handle_155307(regs); break;=20 case 0x08: handle_155308(regs); break;=20 case 0x0a: handle_15530a(regs); break;=20 case 0x0b: handle_15530b(regs); break;=20 case 0x0e: handle_15530e(regs); break;=20 case 0x0f: handle_15530f(regs); break;=20 case 0x10: handle_155310(regs); break;=20 default: handle_1553XX(regs); break;=20 }=20 }=20 // APM Set Power State=20 static void=20 handle_155307(struct bregs *regs)=20 {=20 if (regs->bx !=3D 1) {=20 set_success(regs);=20 return;=20 }=20 switch (regs->cx) {=20 case 1:=20 dprintf(1, "APM standby request\n");=20 break;=20 case 2:=20 dprintf(1, "APM suspend request\n");=20 break;=20 case 3:=20 apm_shutdown();=20 break;=20 }=20 set_success(regs);=20 }=20 void=20 apm_shutdown(void)=20 {=20 u16 pm1a_cnt =3D GET_GLOBAL(acpi_pm1a_cnt);=20 if (pm1a_cnt)=20 outw(0x2000, pm1a_cnt);=20 irq_disable();=20 for (;;)=20 hlt();=20 }=20 </snip>=20 So, QEMU issues an internal ACPI request to power off the system when user = issues a power off in 32-bit protected mode.=20 However, the real mode interface of APM module works fine in VirtualBox. Wh= en user issues power off command and since this is not 32-bit protected mod= e the function =E2=80=98apm_pm32_entry=E2=80=99 is not called instead call = comes to function =E2=80=98apm_function=E2=80=99 with AL set to 0x7 and CX = set to 0x3:=20 <snip>=20 case APM_SET_PWR:=20 // @todo: validate device ID=20 // @todo: validate current connection state=20 switch (CX) {=20 case APM_PS_STANDBY:=20 apm_out_str("Standby", APM_PORT);=20 break;=20 case APM_PS_SUSPEND:=20 apm_out_str("Suspend", APM_PORT);=20 break;=20 case APM_PS_OFF:=20 apm_out_str("Shutdown", APM_PORT); /* Should not return. */=20 break;=20 default:=20 SET_AH(APM_ERR_INVAL_PARAM);=20 SET_CF();=20 }=20 break;=20 </snip>=20 In this case the code writes a string =E2=80=9CShutdown=E2=80=9D on port AP= M_PORT i.e 0x8900. This port number is polled by VirtualBox Bochs BIOS. On = receiving data on this port the function =E2=80=98pcbiosIOPortWrite=E2=80= =99 in file =E2=80=98DevPcBios.cpp=E2=80=99 is called. It handles it as fol= lows:=20 <snip>=20 /*=20 * Bochs BIOS shutdown request.=20 */=20 if (cb =3D=3D 1 && Port =3D=3D 0x8900)=20 {=20 static const unsigned char szShutdown[] =3D "Shutdown";=20 PDEVPCBIOS pThis =3D PDMINS_2_DATA(pDevIns, PDEVPCBIOS);=20 if (u32 =3D=3D szShutdown[pThis->iShutdown])=20 {=20 pThis->iShutdown++;=20 if (pThis->iShutdown =3D=3D 8)=20 {=20 pThis->iShutdown =3D 0;=20 LogRel(("PcBios: 8900h shutdown request\n"));=20 return PDMDevHlpVMPowerOff(pDevIns);=20 }=20 }=20 else=20 pThis->iShutdown =3D 0;=20 return VINF_SUCCESS;=20 }=20 </snip>=20 As we can see it issues a power off request if the string received on port = is =E2=80=9CShutdown=E2=80=9D.=20 If you can provide your inputs on following points then it will be very hel= pful to u=20 1. Have you come across a similar issue and what would be your suggesti= on on fixing this?=20 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. Ple= ase suggest?=20 Regards,=20 Ashutosh=20 +91 9899653573=20
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?647512314.1508652.1473926473830.JavaMail.zimbra>