155211Smsmith/*-
255211Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
355211Smsmith * All rights reserved.
455211Smsmith *
555211Smsmith * Redistribution and use in source and binary forms, with or without
655211Smsmith * modification, are permitted provided that the following conditions
755211Smsmith * are met:
855211Smsmith * 1. Redistributions of source code must retain the above copyright
955211Smsmith *    notice, this list of conditions and the following disclaimer.
1055211Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1155211Smsmith *    notice, this list of conditions and the following disclaimer in the
1255211Smsmith *    documentation and/or other materials provided with the distribution.
1355211Smsmith *
1455211Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1555211Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655211Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755211Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855211Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955211Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055211Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155211Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255211Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355211Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455211Smsmith * SUCH DAMAGE.
2539441Smsmith */
2639441Smsmith
27119482Sobrien#include <sys/cdefs.h>
28119482Sobrien__FBSDID("$FreeBSD: stable/11/stand/i386/libi386/time.c 329099 2018-02-10 04:37:44Z kevans $");
29119482Sobrien
3039441Smsmith#include <stand.h>
3139441Smsmith#include <btxv86.h>
3264187Sjhb#include "bootstrap.h"
3364187Sjhb#include "libi386.h"
3439441Smsmith
35329099Skevanstime_t		getsecs(void);
36179825Sollistatic int	bios_seconds(void);
37179825Solli
3839441Smsmith/*
39179825Solli * Return the BIOS time-of-day value.
4039441Smsmith *
4139441Smsmith * XXX uses undocumented BCD support from libstand.
4239441Smsmith */
43179825Sollistatic int
44179825Sollibios_seconds(void)
4539441Smsmith{
4664187Sjhb    int			hr, minute, sec;
4739441Smsmith
4839441Smsmith    v86.ctl = 0;
4939441Smsmith    v86.addr = 0x1a;		/* int 0x1a, function 2 */
5039441Smsmith    v86.eax = 0x0200;
5139441Smsmith    v86int();
5239441Smsmith
5339441Smsmith    hr = bcd2bin((v86.ecx & 0xff00) >> 8);	/* hour in %ch */
5464187Sjhb    minute = bcd2bin(v86.ecx & 0xff);		/* minute in %cl */
5539441Smsmith    sec = bcd2bin((v86.edx & 0xff00) >> 8);	/* second in %dh */
5639441Smsmith
57179825Solli    return (hr * 3600 + minute * 60 + sec);
58179825Solli}
59179825Solli
60179825Solli/*
61179825Solli * Return the time in seconds since the beginning of the day.
62179825Solli *
63179825Solli * Some BIOSes (notably qemu) don't correctly read the RTC
64179825Solli * registers in an atomic way, sometimes returning bogus values.
65179825Solli * Therefore we "debounce" the reading by accepting it only when
66228782Sed * we got 8 identical values in succession.
67179825Solli *
68179825Solli * If we pass midnight, don't wrap back to 0.
69179825Solli */
70179825Sollitime_t
71179825Sollitime(time_t *t)
72179825Solli{
73179825Solli    static time_t lasttime;
74179825Solli    time_t now, check;
75228782Sed    int same, try;
76179825Solli
77228782Sed    same = try = 0;
78179825Solli    check = bios_seconds();
79179825Solli    do {
80179825Solli	now = check;
81179825Solli	check = bios_seconds();
82228782Sed	if (check != now)
83228782Sed	    same = 0;
84228782Sed    } while (++same < 8 && ++try < 1000);
85179825Solli
8639441Smsmith    if (now < lasttime)
8739441Smsmith	now += 24 * 3600;
8839441Smsmith    lasttime = now;
8939441Smsmith
9039441Smsmith    if (t != NULL)
9139441Smsmith	*t = now;
9239441Smsmith    return(now);
9339441Smsmith}
9439441Smsmith
95329099Skevanstime_t
96329099Skevansgetsecs(void)
97329099Skevans{
98329099Skevans	time_t n = 0;
99329099Skevans	time(&n);
100329099Skevans	return n;
101329099Skevans}
102329099Skevans
10339441Smsmith/*
10439441Smsmith * Use the BIOS Wait function to pause for (period) microseconds.
10539441Smsmith *
10639441Smsmith * Resolution of this function is variable, but typically around
10739441Smsmith * 1ms.
10839441Smsmith */
10939441Smsmithvoid
11039441Smsmithdelay(int period)
11139441Smsmith{
11239441Smsmith    v86.ctl = 0;
11340209Speter    v86.addr = 0x15;		/* int 0x15, function 0x86 */
11439441Smsmith    v86.eax = 0x8600;
11539441Smsmith    v86.ecx = period >> 16;
11639441Smsmith    v86.edx = period & 0xffff;
11739441Smsmith    v86int();
11839441Smsmith}
119