blob: d3a77eb1967e0790a879c2b2ef8163b95a5dc1ee [file] [log] [blame]
/**************************************************************************
This is a component of the hwclock program.
This file contains the code for accessing the hardware clock via
direct I/O (kernel-style input and output operations) as opposed
to via a device driver.
MAINTENANCE NOTES
Here is some information on how the Hardware Clock works, from
unknown source and authority. In theory, the specification for this
stuff is the specification of Motorola's MC146818A clock chip, used
in the early ISA machines. Subsequent machines should have copied
its function exactly. In reality, though, the copies are inexact
and the MC146818A itself may fail to implement its specifications,
and we have just have to work with whatever is there (actually,
anything that Windows works with, because that's what determines
whether broken hardware has to be fixed!).
i386 CMOS starts out with 14 bytes clock data
alpha has something similar, but with details
depending on the machine type.
byte 0: seconds (0-59)
byte 2: minutes (0-59)
byte 4: hours (0-23 in 24hr mode,
1-12 in 12hr mode, with high bit unset/set if am/pm)
byte 6: weekday (1-7, Sunday=1)
byte 7: day of the month (1-31)
byte 8: month (1-12)
byte 9: year (0-99)
Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set
The clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set
The clock is undefined (being updated) if bit 7 of byte 10 is set.
The clock is frozen (to be updated) by setting bit 7 of byte 11
Bit 7 of byte 14 indicates whether the CMOS clock is reliable:
it is 1 if RTC power has been good since this bit was last read;
it is 0 when the battery is dead and system power has been off.
The century situation is messy:
Usually byte 50 (0x32) gives the century (in BCD, so 0x19 or 0x20 in
pure binary), but IBM PS/2 has (part of) a checksum there and uses
byte 55 (0x37). Sometimes byte 127 (0x7f) or Bank 1, byte 0x48
gives the century. The original RTC will not access any century
byte; some modern versions will. If a modern RTC or BIOS increments
the century byte it may go from 0x19 to 0x20, but in some buggy
cases 0x1a is produced.
CMOS byte 10 (clock status register A) has 3 bitfields:
bit 7: 1 if data invalid, update in progress (read-only bit)
(this is raised 224 us before the actual update starts)
6-4 select base frequency
010: 32768 Hz time base (default)
111: reset
all other combinations are manufacturer-dependent
(e.g.: DS1287: 010 = start oscillator, anything else = stop)
3-0 rate selection bits for interrupt
0000 none
0001, 0010 give same frequency as 1000, 1001
0011 122 microseconds (minimum, 8192 Hz)
.... each increase by 1 halves the frequency, doubles the period
1111 500 milliseconds (maximum, 2 Hz)
0110 976.562 microseconds (default 1024 Hz)
Avoid setting the RTC clock within 2 seconds of the day rollover
that starts a new month or enters daylight saving time.
****************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if defined(__i386__) || defined(__alpha__)
#include <asm/io.h> /* for inb, outb */
#else
void outb(int a, int b){}
int inb(int c){ return 0; }
#endif
#include "hwclock.h"
#define BCD_TO_BIN(val) (((val)&0x0f) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
/*----------------------------------------------------------------------------
ATOMIC_TOP and ATOMIC_BOTTOM are wierd macros that help us to do
atomic operations when we do ugly low level I/O.
You put ATOMIC_TOP above some code and ATOMIC_BOTTOM below it and
it makes sure all the enclosed code executes without interruption
by some other process (and, in some cases, even the kernel).
These work fundamentally differently depending on the machine
architecture. In the case of a x86, it simply turns interrupts off
at the top and turns them back on at the bottom.
For Alpha, we can't mess with interrupts (we shouldn't for x86
either, but at least it tends to work!), so instead we start a loop
at the top and close it at the bottom. This loop repeats the
enclosed code until the upper 32 bits of the cycle counter are the
same before and after. That means there was no context change
while the enclosed code was executing.
For other architectures, we do nothing, and the atomicity is only
feigned.
-----------------------------------------------------------------------------*/
#if defined(__i386__)
#define ATOMIC_TOP \
{ \
const bool interrupts_were_enabled = interrupts_enabled; \
__asm__ volatile ("cli"); \
interrupts_enabled = FALSE;
#define ATOMIC_BOTTOM \
if (interrupts_were_enabled) { \
__asm__ volatile ("sti"); \
interrupts_enabled = TRUE; \
} \
}
#elif defined(__alpha__)
#define ATOMIC_TOP \
{ \
unsigned long ts1, ts2, n; \
n = 0; \
do { \
asm volatile ("rpcc %0" : "r="(ts1));
#define ATOMIC_BOTTOM \
asm volatile ("rpcc %0" : "r="(ts2)); \
n++; \
} while ((ts1 ^ ts2) >> 32 != 0); \
}
#else
#define ATOMIC_BOTTOM
#define ATOMIC_TOP
#endif
#if defined(__i386__) || defined(__alpha__)
/* The following are just constants. Oddly, this program will not
compile if the inb() and outb() functions use something even
slightly different from these variables. This is probably at least
partially related to the fact that __builtin_constant_p() doesn't
work (is never true) in an inline function. See comment to this
effect in asm/io.h.
*/
static unsigned short clock_ctl_addr = 0x70;
static unsigned short clock_data_addr = 0x71;
#endif
static bool interrupts_enabled;
/* Interrupts are enabled as normal. We, unfortunately, turn interrupts
on the machine off in some places where we do the direct ISA accesses
to the Hardware Clock. It is in extremely poor form for a user space
program to do this, but that's the price we have to pay to run on an
ISA machine without the rtc driver in the kernel.
Code which turns interrupts off uses this value to determine if they
need to be turned back on.
*/
void
assume_interrupts_enabled(void) {
interrupts_enabled = TRUE;
}
static int
i386_iopl(const int level) {
/*----------------------------------------------------------------------------
When compiled for an Intel target, this is just the iopl() kernel call.
When compiled for any other target, this is a dummy function.
We do it this way in order to keep the conditional compilation stuff
out of the way so it doesn't mess up readability of the code.
-----------------------------------------------------------------------------*/
#ifdef __i386__
extern int iopl(int level);
return iopl(level);
#else
return -1;
#endif
}
bool
uf_bit_needed(const bool user_wants_uf) {
/*----------------------------------------------------------------------------
Return true iff the UIP bit doesn't work on this hardware clock, so
we will need to use the UF bit to synchronize with the clock (if in
fact we synchronize using direct I/O to the clock).
To wit, we need to use the UF bit on a DEC Alpha PC164/LX164/SX164.
Or, of course, if the user told us to.
-----------------------------------------------------------------------------*/
bool retval;
if (user_wants_uf) retval = TRUE;
else {
if (alpha_machine && (
is_in_cpuinfo("system variation", "PC164") ||
is_in_cpuinfo("system variation", "LX164") ||
is_in_cpuinfo("system variation", "SX164")))
retval = TRUE;
else retval = FALSE;
}
if (debug && retval)
printf("We will be using the UF bit instead of the usual "
"UIP bit to synchronize with the clock, as required on "
"certain models of DEC Alpha.\n");
return retval;
}
int
zero_year(const bool arc_opt, const bool srm_opt) {
/*----------------------------------------------------------------------------
Return the year of the century (e.g. 0) to which a zero value in
the year register of the hardware clock applies (or at least what
we are to assume -- nobody can say for sure!)
'arc_opt' and 'srm_opt' are the true iff the user specified the
corresponding invocation option to instruct us that the machine is an
Alpha with ARC or SRM console time.
A note about hardware clocks:
ISA machines are simple: the year register is a year-of-century
register, so the zero year is zero. On Alphas, we may see 1980 or
1952 (Digital Unix?) or 1958 (ALPHA_PRE_V1_2_SRM_CONSOLE)
-----------------------------------------------------------------------------*/
int retval; /* our return value */
if (arc_opt || srm_opt) {
/* User is telling us what epoch his machine uses. Believe it. */
if (arc_opt) retval = 0;
else retval = 0;
} else {
unsigned long kernel_epoch;
char *reason; /* malloc'ed */
get_epoch(&kernel_epoch, &reason);
if (reason == NULL) retval = kernel_epoch;
else {
/* OK, the user doesn't know and the kernel doesn't know;
figure it out from the machine model
*/
free(reason); /* Don't care about kernel's excuses */
/* See whether we are dealing with SRM or MILO, as they have
different "epoch" ideas. */
if (is_in_cpuinfo("system serial number", "MILO")) {
if (debug) printf("booted from MILO\n");
/* See whether we are dealing with a RUFFIAN aka UX, as they
have REALLY different TOY (TimeOfYear) format: BCD, and not
an ARC-style epoch. BCD is detected dynamically, but we
must NOT adjust like ARC.
*/
if (is_in_cpuinfo("system type", "Ruffian")) {
if (debug) printf("Ruffian BCD clock\n");
retval = 0;
} else {
if (debug) printf("Not Ruffian BCD clock\n");
retval = 80;
}
} else {
if (debug) printf("Not booted from MILO\n");
retval = 0;
}
}
}
return retval;
}
static inline unsigned char
hclock_read(const unsigned char reg, const int dev_port) {
/*---------------------------------------------------------------------------
Relative byte 'reg' of the Hardware Clock value.
Get this with direct CPU I/O instructions. If 'dev_port' is not -1,
use the /dev/port device driver (via the 'dev_port' file descriptor)
to do this I/O. Otherwise, use the kernel's inb()/outb() facility.
On a system without the inb()/outb() facility, if 'dev_port' is -1,
just return 0.
Results undefined if 'reg' is out of range.
---------------------------------------------------------------------------*/
unsigned char ret;
ATOMIC_TOP
if (dev_port >= 0) {
const unsigned char v = reg | 0x80;
lseek(dev_port, 0x170, 0);
write(dev_port, &v, 1);
lseek(dev_port, 0x171, 0);
read(dev_port, &ret, 1);
} else {
#if defined(__i386__) || defined(__alpha__)
/* & 0x7f ensures that we are not disabling NMI while we read.
Setting on Bit 7 here would disable NMI
Various docs suggest that one should disable NMI while
reading/writing CMOS data, and enable it again afterwards.
This would yield the sequence
outb (reg | 0x80, 0x70);
val = inb(0x71);
outb (0x0d, 0x70); // 0x0d: random read-only location
Other docs state that "any write to 0x70 should be followed
by an action to 0x71 or the RTC wil be left in an unknown state".
Most docs say that it doesn't matter at all what one does.
*/
outb(reg & 0x7f, clock_ctl_addr);
ret = inb(clock_data_addr);
#else
ret = 0;
#endif
}
ATOMIC_BOTTOM
return ret;
}
static inline void
hclock_write(unsigned char reg, unsigned char val, const int dev_port) {
/*----------------------------------------------------------------------------
Set relative byte 'reg' of the Hardware Clock value to 'val'.
Do this with the kernel's outb() function if 'dev_port' is -1, but
if not, use the /dev/port device (via the 'dev_port' file descriptor),
which is almost the same thing.
On a non-ISA, non-Alpha machine, if 'dev_port' is -1, do nothing.
----------------------------------------------------------------------------*/
if (dev_port >= 0) {
unsigned char v;
v = reg | 0x80;
lseek(dev_port, 0x170, 0);
write(dev_port, &v, 1);
v = (val & 0xff);
lseek(dev_port, 0x171, 0);
write(dev_port, &v, 1);
} else {
#if defined(__i386__) || defined(__alpha__)
/* & 0x7f ensures that we are not disabling NMI while we read.
Setting on Bit 7 here would disable NMI
*/
outb(reg & 0x7f, clock_ctl_addr);
outb(val, clock_data_addr);
#endif
}
}
static inline int
hardware_clock_busy(const int dev_port, const bool use_uf_bit) {
/*----------------------------------------------------------------------------
Return whether the hardware clock is in the middle of an update
(which happens once each second).
Use the clock's UIP bit (bit 7 of Control Register A) to tell
unless 'use_uf_bit' is true, in which case use the UF bit (bit 4 of
Control Register C).
-----------------------------------------------------------------------------*/
return
use_uf_bit ? (hclock_read(12, dev_port) & 0x10) :
(hclock_read(10, dev_port) & 0x80);
}
void
synchronize_to_clock_tick_ISA(int *retcode_p, const int dev_port,
const bool use_uf_bit) {
/*----------------------------------------------------------------------------
Same as synchronize_to_clock_tick(), but just for ISA.
-----------------------------------------------------------------------------*/
int i; /* local loop index */
/* Wait for rise. Should be within a second, but in case something
weird happens, we have a limit on this loop to reduce the impact
of this failure.
*/
for (i = 0;
!hardware_clock_busy(dev_port, use_uf_bit) && (i < 10000000);
i++);
if (i >= 10000000) *retcode_p = 1;
else {
/* Wait for fall. Should be within 2.228 ms. */
for (i = 0;
hardware_clock_busy(dev_port, use_uf_bit) && (i < 1000000);
i++);
if (i >= 10000000) *retcode_p = 1;
else *retcode_p = 0;
}
}
void
read_hardware_clock_isa(struct tm *tm, const int dev_port,
int hc_zero_year) {
/*----------------------------------------------------------------------------
Read the hardware clock and return the current time via <tm> argument.
Assume we have an ISA machine and read the clock directly with CPU I/O
instructions. If 'dev_port' isn't -1, use the /dev/port facility to
do this I/O. Otherwise, use the kernel's inb()/outb() service.
This function is not totally reliable. It takes a finite and
unpredictable amount of time to execute the code below. During that
time, the clock may change and we may even read an invalid value in
the middle of an update. We do a few checks to minimize this
possibility, but only the kernel can actually read the clock
properly, since it can execute code in a short and predictable
amount of time (by turning off interrupts).
In practice, the chance of this function returning the wrong time is
extremely remote.
-----------------------------------------------------------------------------*/
bool got_time;
/* We've successfully read a time from the Hardware Clock */
int attempts;
/* Number of times we've tried to read the clock. This only
matters because we will give up (and proceed with garbage in
variables) rather than hang if something is broken and we are
never able to read the clock
*/
int hclock_sec = 0, hclock_min = 0, hclock_hour = 0, hclock_wday = 0,
hclock_mon = 0, hclock_mday = 0, hclock_year = 0;
/* The values we got from the Hardware Clock's registers, assuming
they are in pure binary.
*/
int status = 0; /* Hardware Clock status register, as if pure binary */
int adjusted_year;
int ampmhour;
int pmbit;
got_time = FALSE;
attempts = 0; /* initial value */
while (!got_time && attempts++ < 1000000) {
/* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress
(UIP) bit, which is on while and 244 uS before the Hardware Clock
updates itself. It updates the counters individually, so reading
them during an update would produce garbage. The update takes 2mS,
so we could be spinning here that long waiting for this bit to turn
off.
Furthermore, it is pathologically possible for us to be in this
code so long that even if the UIP bit is not on at first, the
clock has changed while we were running. We check for that too,
and if it happens, we start over.
*/
if ((hclock_read(10, dev_port) & 0x80) == 0) {
/* No clock update in progress, go ahead and read */
status = hclock_read(11, dev_port);
hclock_sec = hclock_read(0, dev_port);
hclock_min = hclock_read(2, dev_port);
hclock_hour = hclock_read(4, dev_port);
hclock_wday = hclock_read(6, dev_port);
hclock_mday = hclock_read(7, dev_port);
hclock_mon = hclock_read(8, dev_port);
hclock_year = hclock_read(9, dev_port);
/* Unless the clock changed while we were reading, consider this
a good clock read .
*/
if (hclock_sec == hclock_read(0, dev_port)) got_time = TRUE;
/* Yes, in theory we could have been running for 60 seconds and
the above test wouldn't work!
*/
}
}
if (!(status & 0x04)) {
/* The hardware clock is in BCD mode. This is normal. */
tm->tm_sec = BCD_TO_BIN(hclock_sec);
tm->tm_min = BCD_TO_BIN(hclock_min);
ampmhour = BCD_TO_BIN(hclock_hour & 0x7f);
pmbit = hclock_hour & 0x80;
tm->tm_wday = BCD_TO_BIN(hclock_wday) - 1; /* Used to be 3. Why?? */
tm->tm_mday = BCD_TO_BIN(hclock_mday);
tm->tm_mon = BCD_TO_BIN(hclock_mon) - 1;
adjusted_year = BCD_TO_BIN(hclock_year);
} else {
/* The hardware clock registers are in pure binary format. */
tm->tm_sec = hclock_sec;
tm->tm_min = hclock_min;
ampmhour = hclock_hour & 0x7f;
pmbit = hclock_hour & 0x80;
tm->tm_wday = hclock_wday - 1; /* Used to be 3. Why?? */
tm->tm_mday = hclock_mday;
tm->tm_mon = hclock_mon - 1;
adjusted_year = hclock_year;
}
if (!(status & 0x02)) {
/* Clock is in 12 hour (am/pm) mode. This is unusual. */
if (pmbit == 0x80) {
if (ampmhour == 12) tm->tm_hour = 12;
else tm->tm_hour = 12 + ampmhour;
} else {
if (ampmhour ==12) tm->tm_hour = 0;
else tm->tm_hour = ampmhour;
}
} else {
/* Clock is in 24 hour mode. This is normal. */
tm->tm_hour = ampmhour;
}
/* We don't use the century byte (Byte 50) of the Hardware Clock.
Here's why: It didn't exist in the original ISA specification,
so old machines don't have it, and even some new ones don't.
Some machines, including the IBM Valuepoint 6387-X93, use that
byte for something else. Some machines have the century in
Byte 55.
Furthermore, the Linux standard time data structure doesn't
allow for times beyond about 2037 and no Linux systems were
running before 1937. Therefore, all the century byte could tell
us is that the clock is wrong or this whole program is obsolete!
So we just say if the year of century is less than 37, it's the
2000's, otherwise it's the 1900's.
Alpha machines (some, anyway) don't have this ambiguity
because they do not have a year-of-century register. We
pretend they do anyway, for simplicity and to avoid
recognizing times that can't be represented in Linux standard
time. So even though we already have enough information to
know that the clock says 2050, we will render it as 1950.
*/
{
const int year_of_century = (adjusted_year + hc_zero_year) % 100;
if (year_of_century >= 37) tm->tm_year = year_of_century;
else tm->tm_year = year_of_century + 100;
}
tm->tm_isdst = -1; /* don't know whether it's daylight */
}
void
set_hardware_clock_isa(const struct tm new_tm,
const int hc_zero_year,
const int dev_port,
const bool testing) {
/*----------------------------------------------------------------------------
Set the Hardware Clock to the time (in broken down format)
new_tm. Use direct I/O instructions to what we assume is
an ISA Hardware Clock.
Iff 'dev_port' is -1, use the kernel inb()/outb() service, otherwise
use the /dev/port device (via file descriptor 'dev_port')
to do those I/O instructions.
----------------------------------------------------------------------------*/
unsigned char save_control, save_freq_select;
if (testing)
printf("Not setting Hardware Clock because running in test mode.\n");
else {
int ampmhour;
/* The hour number that goes into the hardware clock, taking into
consideration whether the clock is in 12 or 24 hour mode
*/
int pmbit;
/* Value to OR into the hour register as the am/pm bit */
const int adjusted_year =
(new_tm.tm_year - hc_zero_year)%100;
/* The number that goes in the hardware clock's year register */
int hclock_sec, hclock_min, hclock_hour, hclock_wday, hclock_mon,
hclock_mday, hclock_year;
/* The values we will put, in pure binary, in the Hardware Clock's
registers.
*/
ATOMIC_TOP
save_control = hclock_read(11, dev_port);
/* tell the clock it's being set */
hclock_write(11, (save_control | 0x80), dev_port);
save_freq_select = hclock_read(10, dev_port);
/* stop and reset prescaler */
hclock_write (10, (save_freq_select | 0x70), dev_port);
if (!(save_control & 0x02)) {
/* Clock is in 12 hour (am/pm) mode. This is unusual. */
if (new_tm.tm_hour == 0) {
ampmhour = 12;
pmbit = 0x00;
} else if (new_tm.tm_hour < 12) {
ampmhour = new_tm.tm_hour;
pmbit = 0x00;
} else if (new_tm.tm_hour == 12) {
ampmhour = 12;
pmbit = 0x80;
} else {
ampmhour = new_tm.tm_hour - 12;
pmbit = 0x80;
}
} else {
/* Clock is in 24 hour mode. This is normal. */
ampmhour = new_tm.tm_hour;
pmbit = 0x00;
}
if (!(save_control & 0x04)) {
/* Clock's registers are in BCD. This is normal. */
hclock_sec = BIN_TO_BCD(new_tm.tm_sec);
hclock_min = BIN_TO_BCD(new_tm.tm_min);
hclock_hour = pmbit | BIN_TO_BCD(ampmhour);
hclock_wday = BIN_TO_BCD(new_tm.tm_wday + 1); /* Used to be 3. Why??*/
hclock_mday = BIN_TO_BCD(new_tm.tm_mday);
hclock_mon = BIN_TO_BCD(new_tm.tm_mon + 1);
hclock_year = BIN_TO_BCD(adjusted_year);
} else {
/* Clock's registers are in pure binary. This is unusual. */
hclock_sec = new_tm.tm_sec;
hclock_min = new_tm.tm_min;
hclock_hour = pmbit | ampmhour;
hclock_wday = new_tm.tm_wday + 1; /* Used to be 3. Why?? */
hclock_mday = new_tm.tm_mday;
hclock_mon = new_tm.tm_mon + 1;
hclock_year = adjusted_year;
}
hclock_write(0, hclock_sec, dev_port);
hclock_write(2, hclock_min, dev_port);
hclock_write(4, hclock_hour, dev_port);
hclock_write(6, hclock_wday, dev_port);
hclock_write(7, hclock_mday, dev_port);
hclock_write(8, hclock_mon, dev_port);
hclock_write(9, hclock_year, dev_port);
/* We don't set the century byte (usually Byte 50) because it isn't
always there. (see further comments in read_hardware_clock_isa).
In previous releases, we did.
*/
/* The kernel sources, linux/arch/i386/kernel/time.c, have the
following comment:
The following flags have to be released exactly in this order,
otherwise the DS12887 (popular MC146818A clone with integrated
battery and quartz) will not reset the oscillator and will not
update precisely 500 ms later. You won't find this mentioned
in the Dallas Semiconductor data sheets, but who believes data
sheets anyway ... -- Markus Kuhn
Hence, they will also be done in this order here.
faith@cs.unc.edu, Thu Nov 9 08:26:37 1995
*/
hclock_write (11, save_control, dev_port);
hclock_write (10, save_freq_select, dev_port);
ATOMIC_BOTTOM
}
}
void
get_inb_outb_privilege(const enum clock_access_method clock_access,
bool * const no_auth_p) {
if (clock_access == ISA) {
const int rc = i386_iopl(3);
if (rc != 0) {
fprintf(stderr, MYNAME " is unable to get I/O port access. "
"I.e. iopl(3) returned nonzero return code %d.\n"
"This is often because the program isn't running "
"with superuser privilege, which it needs.\n",
rc);
*no_auth_p = TRUE;
} else *no_auth_p = FALSE;
} else *no_auth_p = FALSE;
}
void
get_dev_port_access(const enum clock_access_method clock_access,
int * dev_port_p) {
if (clock_access == DEV_PORT) {
/* Get the /dev/port file open */
*dev_port_p = open("/dev/port", O_RDWR);
if (*dev_port_p < 0) {
fprintf(stderr, MYNAME "is unable to open the /dev/port file. "
"I.e. open() of the file failed with errno = %s (%d).\n"
"Run with the --debug option and check documentation "
"to find out why we are trying "
"to use /dev/port instead of some other means to access "
"the Hardware Clock.",
strerror(errno), errno);
}
} else *dev_port_p = 0;
}