VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/apm.c

Last change on this file was 100456, checked in by vboxsync, 10 months ago

BIOS: Added a way for the APM BIOS to halt the virtual CPU through port I/O instead of HLT to solve problems with obstinate guests (see bugref:6549).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.7 KB
Line 
1/* $Id: apm.c 100456 2023-07-10 13:46:21Z vboxsync $ */
2/** @file
3 * APM BIOS support. Implements APM version 1.2.
4 */
5
6/*
7 * Copyright (C) 2004-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <stdint.h>
29#include <string.h>
30#include "biosint.h"
31#include "inlines.h"
32#include "VBox/bios.h"
33
34#if DEBUG_APM
35# define BX_DEBUG_APM(...) BX_DEBUG(__VA_ARGS__)
36#else
37# define BX_DEBUG_APM(...)
38#endif
39
40/* Implemented in assembly. */
41extern void apm_pm16_entry(void);
42#pragma aux apm_pm16_entry "*"
43
44#if VBOX_BIOS_CPU >= 80386
45extern void apm_pm32_entry(void);
46#pragma aux apm_pm32_entry "*"
47#endif
48
49/* APM function codes. */
50enum apm_func {
51 APM_CHECK = 0x00, /* APM Installation Check */
52 APM_RM_CONN = 0x01, /* APM Real Mode Interface Connect */
53 APM_PM_CONN = 0x02, /* APM Protected Mode 16-bit Interface Connect */
54 APM_32_CONN = 0x03, /* APM Protected Mode 32-bit Interface Connect */
55 APM_DISCONN = 0x04, /* APM Interface Disconnect */
56 APM_IDLE = 0x05, /* CPU Idle */
57 APM_BUSY = 0x06, /* CPU Busy */
58 APM_SET_PWR = 0x07, /* Set Power State */
59 APM_ENBL_PM = 0x08, /* Enable/Disable Power Management */
60 APM_SET_DFL = 0x09, /* Restore APM BIOS Power-On Defaults */
61 APM_STATUS = 0x0A, /* Get Power Status */
62 APM_GET_EVT = 0x0B, /* Get PM Event */
63 APM_GET_PWR = 0x0C, /* Get Power State */
64 APM_DEVPM = 0x0D, /* Enable/Disable Device Power Management */
65 APM_DRV_VER = 0x0E, /* APM Driver Version */
66 APM_ENGAGE = 0x0F, /* Engage/Disengage Power Management */
67 APM_GET_CAP = 0x10 /* Get Capabilities */
68};
69
70enum apm_error {
71 APM_ERR_PM_DISABLED = 0x01, /* Power Management functionality disabled */
72 APM_ERR_RM_INUSE = 0x02, /* Real mode interface connection already established */
73 APM_ERR_NOT_CONN = 0x03, /* Interface not connected */
74 APM_ERR_PM_16_INUSE = 0x05, /* 16-bit protected mode interface connection already established */
75 APM_ERR_NO_PM_16 = 0x06, /* 16-bit protected mode interface not supported */
76 APM_ERR_PM_32_INUSE = 0x07, /* 32-bit protected mode interface connection already established */
77 APM_ERR_NO_PM_32 = 0x08, /* 32-bit protected mode interface not supported */
78 APM_ERR_BAD_DEV_ID = 0x09, /* Unrecognized device ID */
79 APM_ERR_INVAL_PARAM = 0x0A, /* Parameter out of range */
80 APM_ERR_NOT_ENGAGED = 0x0B, /* Interface not engaged */
81 APM_ERR_UNSUPPORTED = 0x0C, /* Function not supported */
82 APM_ERR_NO_RSM_TMR = 0x0D, /* Resume timer disabled */
83 APM_ERR_NO_EVENTS = 0x80 /* No power management events pending */
84};
85
86enum apm_power_state {
87 APM_PS_ENABLED = 0x00, /* APM enabled */
88 APM_PS_STANDBY = 0x01, /* Standby */
89 APM_PS_SUSPEND = 0x02, /* Suspend */
90 APM_PS_OFF = 0x03, /* Suspend */
91};
92
93/// @todo merge with system.c
94#define AX r.gr.u.r16.ax
95#define BX r.gr.u.r16.bx
96#define CX r.gr.u.r16.cx
97#define DX r.gr.u.r16.dx
98#define SI r.gr.u.r16.si
99#define DI r.gr.u.r16.di
100#define BP r.gr.u.r16.bp
101#define SP r.gr.u.r16.sp
102#define FLAGS r.fl.u.r16.flags
103#define EAX r.gr.u.r32.eax
104#define EBX r.gr.u.r32.ebx
105#define ECX r.gr.u.r32.ecx
106#define EDX r.gr.u.r32.edx
107#define ES r.es
108
109#define APM_BIOS_SEG 0xF000 /* Real-mode APM segment. */
110#define APM_BIOS_SEG_LEN 0xFFF0 /* Length of APM segment. */
111
112/* The APM BIOS interface uses 32-bit registers *only* in the 32-bit
113 * protected mode connect call. Rather than saving/restoring 32-bit
114 * registers all the time, simply set the high words of those registers
115 * when necessary.
116 */
117void set_ebx_hi(uint16_t val);
118#pragma aux set_ebx_hi = \
119 ".386" \
120 "shl ebx, 16" \
121 parm [bx] modify exact [bx] nomemory;
122
123void set_esi_hi(uint16_t val);
124#pragma aux set_esi_hi = \
125 ".386" \
126 "shl esi, 16" \
127 parm [si] modify exact [si] nomemory;
128
129
130/* The APM handler has unique requirements. It must be callable from real and
131 * protected mode, both 16-bit and 32-bit. In protected mode, the caller must
132 * ensure that appropriate selectors are available; these only cover the BIOS
133 * code and data, hence the BIOS Data Area or EBDA cannot be accessed. CMOS is
134 * a good place to store information which needs to be accessible from several
135 * different contexts.
136 *
137 * Note that the 32-bit protected-mode handler only needs to thunk down to the
138 * 16-bit code. There's no need for separate 16-bit and 32-bit implementation.
139 */
140
141/* Wrapper to avoid unnecessary inlining. */
142void apm_out_str(const char *s)
143{
144 if (*s)
145 out_ctrl_str_asm(VBOX_BIOS_SHUTDOWN_PORT, s);
146}
147
148void BIOSCALL apm_function(sys_regs_t r)
149{
150 BX_DEBUG_APM("APM: AX=%04X BX=%04X CX=%04X\n", AX, BX, CX);
151
152 CLEAR_CF(); /* Boldly expect success. */
153 switch (GET_AL()) {
154 case APM_CHECK:
155 AX = 0x0102; /* Version 1.2 */
156 BX = 0x504D; /* 'PM' */
157 CX = 3; /* Bits 0/1: 16-bit/32-bit PM interface */
158 break;
159 case APM_RM_CONN:
160 /// @todo validate device ID
161 /// @todo validate current connection state
162 /// @todo change connection state
163 break;
164#if VBOX_BIOS_CPU >= 80286
165 case APM_PM_CONN:
166 /// @todo validate device ID
167 /// @todo validate current connection state
168 /// @todo change connection state
169 AX = APM_BIOS_SEG; /* 16-bit PM code segment (RM segment base). */
170 BX = (uint16_t)apm_pm16_entry; /* 16-bit PM entry point offset. */
171 CX = APM_BIOS_SEG; /* 16-bit data segment. */
172 SI = APM_BIOS_SEG_LEN; /* 16-bit PM code segment length. */
173 DI = APM_BIOS_SEG_LEN; /* Data segment length. */
174 break;
175#endif
176#if VBOX_BIOS_CPU >= 80386
177 case APM_32_CONN:
178 /// @todo validate device ID
179 /// @todo validate current connection state
180 /// @todo change connection state
181 AX = APM_BIOS_SEG; /* 32-bit PM code segment (RM segment base). */
182 BX = (uint16_t)apm_pm32_entry; /* 32-bit entry point offset. */
183 CX = APM_BIOS_SEG; /* 16-bit code segment. */
184 DX = APM_BIOS_SEG; /* 16-bit data segment. */
185 SI = APM_BIOS_SEG_LEN; /* 32-bit code segment length. */
186 DI = APM_BIOS_SEG_LEN; /* Data segment length. */
187 set_ebx_hi(0);
188 set_esi_hi(APM_BIOS_SEG_LEN); /* 16-bit code segment length. */
189 break;
190#endif
191 case APM_IDLE:
192 int_enable(); /* Simply halt the CPU with interrupts enabled. */
193 halt();
194 break;
195 case APM_SET_PWR:
196 /// @todo validate device ID
197 /// @todo validate current connection state
198 switch (CX) {
199 case APM_PS_STANDBY:
200 apm_out_str("Standby");
201 break;
202 case APM_PS_SUSPEND:
203 apm_out_str("Suspend");
204 break;
205 case APM_PS_OFF:
206 apm_out_str("Shutdown"); /* Should not return. */
207 break;
208 default:
209 SET_AH(APM_ERR_INVAL_PARAM);
210 SET_CF();
211 }
212 break;
213 case APM_DRV_VER:
214 AX = 0x0102; /// @todo Not right - must take driver version into account!
215 break;
216 case APM_DISCONN:
217 /// @todo actually perform a disconnect...
218 case APM_BUSY: /* Nothing to do as APM Idle doesn't slow CPU clock. */
219 break;
220 case APM_STATUS:
221 /* We do not attempt to report battery status. */
222 BX = 0x01FF; /* AC line power, battery unknown. */
223 CX = 0x80FF; /* No battery. */
224 DX = 0xFFFF; /* No idea about remaining battery life. */
225 break;
226 case APM_GET_EVT:
227 /// @todo error should be different if interface not connected + engaged
228 SET_AH(APM_ERR_NO_EVENTS); /* PM events don't happen. */
229 SET_CF();
230 break;
231 default:
232 BX_INFO("APM: Unsupported function AX=%04X BX=%04X called\n", AX, BX);
233 SET_AH(APM_ERR_UNSUPPORTED);
234 SET_CF();
235 }
236}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use