VirtualBox

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

Last change on this file since 66130 was 66130, checked in by vboxsync, 8 years ago

Devices/PC/DevPcBios+Devices/PC/BIOS/apm.c: relocate the APM shutdown port to the chipset range (avoiding future trouble with PCI resource allocation), including backward compatibility support (so that old VMs with saved state are keeping the old port until they're reset).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette