1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
; $Id:
;; @file
; Protected-mode APM implementation.
;
;
; Copyright (C) 2006-2023 Oracle and/or its affiliates.
;
; This file is part of VirtualBox base platform packages, as
; available from https://www.virtualbox.org.
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation, in version 3 of the
; License.
;
; This program is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
; General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, see <https://www.gnu.org/licenses>.
;
; SPDX-License-Identifier: GPL-3.0-only
;
include commondefs.inc
;; 16-bit protected mode APM entry point
_TEXT segment public 'CODE'
extern _apm_function:near ; implemented in C code
public apm_pm16_entry
SET_DEFAULT_CPU_286
; 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 routine. Function code in AL; it is assumed that AL >= 4.
; Caller must preserve BP.
;
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
;
; Windows 3.1 POWER.DRV in Standard mode calls into APM
; with CPL=3. If that happens, the HLT instruction will fault
; and Windows will crash. To prevent that, we check the CPL
; and do nothing (better than crashing).
;
push cs
pop ax
test ax, 3 ; CPL > 0?
jnz apmw_success
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
;; 16-bit protected mode APM entry point
;; According to the APM spec, only CS (16-bit code selector) is defined.
;; The data selector can be derived from it.
apm_pm16_entry:
mov ah, 2 ; mark as originating in 16-bit PM
; fall through
apm_pm16_entry_from_32:
push ds ; save registers
push bp
push cs
pop bp
add bp, 8 ; calculate data selector
mov ds, bp ; load data segment
call apm_worker ; call APM handler
pop bp
pop ds ; restore registers
retf ; return to caller - 16-bit return
; even to 32-bit thunk!
_TEXT ends
if VBOX_BIOS_CPU ge 80386
.386
BIOS32 segment public 'CODE' use32
public apm_pm32_entry
;; 32-bit protected mode APM entry point and thunk
;; According to the APM spec, only CS (32-bit) is defined. 16-bit code
;; selector and the data selector can be derived from it.
;; WARNING: To simplify matters, we use 16-bit far return to go from 32-bit
;; code to 16-bit and back. As a consequence, the 32-bit APM code must lie
;; below 64K boundary in the 32-bit APM code segment.
apm_pm32_entry:
push ebp ; ebp is not used by APM
mov bp, cs ; return address for 16-bit code
push bp
mov ebp, apm_pm32_back
push bp ; Note: 16:16 address!
push cs
pop ebp
add ebp, 8 ; calculate 16-bit code selector
push bp ; push 16-bit code selector
mov ebp, apm_pm16_entry_from_32
push bp ; push 16-bit offset
mov ah, 3 ; mark as originating in 32-bit PM
db 66h ; force a 16-bit return
retf ; off to 16-bit code...
apm_pm32_back: ; return here from 16-bit code
pop ebp ; restore scratch register
retf
BIOS32 ends
endif ; 32-bit code
end
|