VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.8 KB
Line 
1/*
2 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
3 *
4 * This file is part of VirtualBox base platform packages, as
5 * available from https://www.virtualbox.org.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, in version 3 of the
10 * License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses>.
19 *
20 * SPDX-License-Identifier: GPL-3.0-only
21 * --------------------------------------------------------------------
22 *
23 * This code is based on:
24 *
25 * ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
26 *
27 * Copyright (C) 2002 MandrakeSoft S.A.
28 *
29 * MandrakeSoft S.A.
30 * 43, rue d'Aboukir
31 * 75002 Paris - France
32 * http://www.linux-mandrake.com/
33 * http://www.mandrakesoft.com/
34 *
35 * This library is free software; you can redistribute it and/or
36 * modify it under the terms of the GNU Lesser General Public
37 * License as published by the Free Software Foundation; either
38 * version 2 of the License, or (at your option) any later version.
39 *
40 * This library is distributed in the hope that it will be useful,
41 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 * Lesser General Public License for more details.
44 *
45 * You should have received a copy of the GNU Lesser General Public
46 * License along with this library; if not, write to the Free Software
47 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
48 *
49 */
50
51/*
52 * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
53 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
54 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
55 * a choice of LGPL license versions is made available with the language indicating
56 * that LGPLv2 or any later version may be used, or where a choice of which version
57 * of the LGPL is applied is otherwise unspecified.
58 */
59
60
61#include <stdint.h>
62#include "biosint.h"
63#include "inlines.h"
64
65#if DEBUG_INT1A
66# define BX_DEBUG_INT1A(...) BX_DEBUG(__VA_ARGS__)
67#else
68# define BX_DEBUG_INT1A(...)
69#endif
70
71// for access to RAM area which is used by interrupt vectors
72// and BIOS Data Area
73
74typedef struct {
75 uint8_t filler1[0x400];
76 uint8_t filler2[0x6c];
77 uint16_t ticks_low;
78 uint16_t ticks_high;
79 uint8_t midnight_flag;
80} bios_data_t;
81
82#define BiosData ((bios_data_t __far *) 0)
83
84void init_rtc(void)
85{
86 outb_cmos(0x0a, 0x26);
87 outb_cmos(0x0b, 0x02);
88 inb_cmos(0x0c);
89 inb_cmos(0x0d);
90}
91
92bx_bool rtc_updating(void)
93{
94 // This function checks to see if the update-in-progress bit
95 // is set in CMOS Status Register A. If not, it returns 0.
96 // If it is set, it tries to wait until there is a transition
97 // to 0, and will return 0 if such a transition occurs. A 1
98 // is returned only after timing out. The maximum period
99 // that this bit should be set is constrained to 244useconds.
100 // The count I use below guarantees coverage or more than
101 // this time, with any reasonable IPS setting.
102
103 uint16_t iter;
104
105 iter = 25000;
106 while (--iter != 0) {
107 if ( (inb_cmos(0x0a) & 0x80) == 0 )
108 return 0;
109 }
110 return 1; // update-in-progress never transitioned to 0
111}
112
113
114extern void eoi_both_pics(void); /* in assembly code */
115#pragma aux eoi_both_pics "*";
116
117void call_int_4a(void);
118#pragma aux call_int_4a = "int 4Ah";
119
120void BIOSCALL int70_function(pusha_regs_t regs, uint16_t ds, uint16_t es, iret_addr_t iret_addr)
121{
122 // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
123 uint8_t registerB = 0, registerC = 0;
124
125 // Check which modes are enabled and have occurred.
126 registerB = inb_cmos( 0xB );
127 registerC = inb_cmos( 0xC );
128
129 if( ( registerB & 0x60 ) != 0 ) {
130 if( ( registerC & 0x20 ) != 0 ) {
131 // Handle Alarm Interrupt.
132 int_enable();
133 call_int_4a();
134 int_disable();
135 }
136 if( ( registerC & 0x40 ) != 0 ) {
137 // Handle Periodic Interrupt.
138
139 if( read_byte( 0x40, 0xA0 ) != 0 ) {
140 // Wait Interval (Int 15, AH=83 or AH=86) active.
141 uint32_t time;
142
143 time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
144 if( time < 0x3D1 ) {
145 // Done waiting.
146 uint16_t segment, offset;
147
148 segment = read_word( 0x40, 0x98 );
149 offset = read_word( 0x40, 0x9A );
150 write_byte( 0x40, 0xA0, 0 ); // Turn off status byte.
151 outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
152 write_byte( segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
153 } else {
154 // Continue waiting.
155 time -= 0x3D1;
156 write_dword( 0x40, 0x9C, time );
157 }
158 }
159 }
160 }
161 eoi_both_pics();
162}
163
164/// @todo the coding style WRT register access is totally inconsistent
165// in the following routines
166
167void BIOSCALL int1a_function(pusha_regs_t regs, uint16_t ds, uint16_t es, iret_addr_t iret_addr)
168{
169 uint8_t val8;
170
171 BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n",
172 regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds);
173 int_enable();
174
175 switch (regs.u.r8.ah) {
176 case 0: // get current clock count
177 int_disable();
178 regs.u.r16.cx = BiosData->ticks_high;
179 regs.u.r16.dx = BiosData->ticks_low;
180 regs.u.r8.al = BiosData->midnight_flag;
181 BiosData->midnight_flag = 0; // reset flag
182 int_enable();
183 // AH already 0
184 ClearCF(iret_addr.flags); // OK
185 break;
186
187 case 1: // Set Current Clock Count
188 int_disable();
189 BiosData->ticks_high = regs.u.r16.cx;
190 BiosData->ticks_low = regs.u.r16.dx;
191 BiosData->midnight_flag = 0; // reset flag
192 int_enable();
193 regs.u.r8.ah = 0;
194 ClearCF(iret_addr.flags); // OK
195 break;
196
197 case 2: // Read CMOS Time
198 if (rtc_updating()) {
199 SetCF(iret_addr.flags);
200 break;
201 }
202
203 regs.u.r8.dh = inb_cmos(0x00); // Seconds
204 regs.u.r8.cl = inb_cmos(0x02); // Minutes
205 regs.u.r8.ch = inb_cmos(0x04); // Hours
206 regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
207 regs.u.r8.ah = 0;
208 regs.u.r8.al = regs.u.r8.ch;
209 ClearCF(iret_addr.flags); // OK
210 break;
211
212 case 3: // Set CMOS Time
213 // Using a debugger, I notice the following masking/setting
214 // of bits in Status Register B, by setting Reg B to
215 // a few values and getting its value after INT 1A was called.
216 //
217 // try#1 try#2 try#3
218 // before 1111 1101 0111 1101 0000 0000
219 // after 0110 0010 0110 0010 0000 0010
220 //
221 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
222 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
223 if (rtc_updating()) {
224 init_rtc();
225 // fall through as if an update were not in progress
226 }
227 outb_cmos(0x00, regs.u.r8.dh); // Seconds
228 outb_cmos(0x02, regs.u.r8.cl); // Minutes
229 outb_cmos(0x04, regs.u.r8.ch); // Hours
230 // Set Daylight Savings time enabled bit to requested value
231 val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
232 // (reg B already selected)
233 outb_cmos(0x0b, val8);
234 regs.u.r8.ah = 0;
235 regs.u.r8.al = val8; // val last written to Reg B
236 ClearCF(iret_addr.flags); // OK
237 break;
238
239 case 4: // Read CMOS Date
240 regs.u.r8.ah = 0;
241 if (rtc_updating()) {
242 SetCF(iret_addr.flags);
243 break;
244 }
245 regs.u.r8.cl = inb_cmos(0x09); // Year
246 regs.u.r8.dh = inb_cmos(0x08); // Month
247 regs.u.r8.dl = inb_cmos(0x07); // Day of Month
248 regs.u.r8.ch = inb_cmos(0x32); // Century
249 regs.u.r8.al = regs.u.r8.ch;
250 ClearCF(iret_addr.flags); // OK
251 break;
252
253 case 5: // Set CMOS Date
254 // Using a debugger, I notice the following masking/setting
255 // of bits in Status Register B, by setting Reg B to
256 // a few values and getting its value after INT 1A was called.
257 //
258 // try#1 try#2 try#3 try#4
259 // before 1111 1101 0111 1101 0000 0010 0000 0000
260 // after 0110 1101 0111 1101 0000 0010 0000 0000
261 //
262 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
263 // My assumption: RegB = (RegB & 01111111b)
264 if (rtc_updating()) {
265 init_rtc();
266 SetCF(iret_addr.flags);
267 break;
268 }
269 outb_cmos(0x09, regs.u.r8.cl); // Year
270 outb_cmos(0x08, regs.u.r8.dh); // Month
271 outb_cmos(0x07, regs.u.r8.dl); // Day of Month
272 outb_cmos(0x32, regs.u.r8.ch); // Century
273 val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
274 outb_cmos(0x0b, val8);
275 regs.u.r8.ah = 0;
276 regs.u.r8.al = val8; // AL = val last written to Reg B
277 ClearCF(iret_addr.flags); // OK
278 break;
279
280 case 6: // Set Alarm Time in CMOS
281 // Using a debugger, I notice the following masking/setting
282 // of bits in Status Register B, by setting Reg B to
283 // a few values and getting its value after INT 1A was called.
284 //
285 // try#1 try#2 try#3
286 // before 1101 1111 0101 1111 0000 0000
287 // after 0110 1111 0111 1111 0010 0000
288 //
289 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
290 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
291 val8 = inb_cmos(0x0b); // Get Status Reg B
292 regs.u.r16.ax = 0;
293 if (val8 & 0x20) {
294 // Alarm interrupt enabled already
295 SetCF(iret_addr.flags); // Error: alarm in use
296 break;
297 }
298 if (rtc_updating()) {
299 init_rtc();
300 // fall through as if an update were not in progress
301 }
302 outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
303 outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
304 outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
305 outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
306 // enable Status Reg B alarm bit, clear halt clock bit
307 outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
308 ClearCF(iret_addr.flags); // OK
309 break;
310
311 case 7: // Turn off Alarm
312 // Using a debugger, I notice the following masking/setting
313 // of bits in Status Register B, by setting Reg B to
314 // a few values and getting its value after INT 1A was called.
315 //
316 // try#1 try#2 try#3 try#4
317 // before 1111 1101 0111 1101 0010 0000 0010 0010
318 // after 0100 0101 0101 0101 0000 0000 0000 0010
319 //
320 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
321 // My assumption: RegB = (RegB & 01010111b)
322 val8 = inb_cmos(0x0b); // Get Status Reg B
323 // clear clock-halt bit, disable alarm bit
324 outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
325 regs.u.r8.ah = 0;
326 regs.u.r8.al = val8; // val last written to Reg B
327 ClearCF(iret_addr.flags); // OK
328 break;
329
330 default:
331 BX_DEBUG_INT1A("int1a: AX=%04x unsupported\n", regs.u.r16.ax);
332 SetCF(iret_addr.flags); // Unsupported
333 }
334}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use