Subject: port-hp300/8824: year 2000 problem with battery backed clock
To: None <gnats-bugs@gnats.netbsd.org>
From: Steve Peurifoy <swp@alumni.rice.edu>
List: netbsd-bugs
Date: 11/18/1999 19:45:58
>Number:         8824
>Category:       port-hp300
>Synopsis:       battery backed clock fails across year 2000 boundary
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-hp300-maintainer (NetBSD/hp300 Portmaster)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Nov 18 19:45:01 1999
>Last-Modified:
>Originator:     
>Organization:
>Release:        1.4K (1999/10/02 sources)
>Environment:
System: NetBSD loop 1.4K NetBSD 1.4K (loop) #1: Thu Nov 18 16:10:13 MST 1999 root@loop:/usr/obj/sys/arch/hp300/compile/loop hp300


>Description:
	An hp300 that is powered down during the transition from 1999/12/31
	to 2000/01/01 (UTC) will have an incorrect system time when it is
	next booted.
>How-To-Repeat:
	TZ=UTC date -n 199912312345
	/sbin/halt
	power down system
	wait > 15 minutes
	power up system - it will complain about a bad date in battery backed
		clock and will set time from root fs superblock.
>Fix:
	The following patch works for me.  Note that the problem will not
	manifest itself if the system is left up during the transition.
	The decade register in the bbc can be set to the value 0xa
	but it will not roll to 0xa from 9.

	This patch also changes a couple of comments apparently written by
	someone who never read the bbc's datasheet and fixes one spelling
	nit.

	The line numbers in this patch won't match up with the file
	in the tree due to some local modifications but there should
	be sufficient context.

Index: clock.c
===================================================================
RCS file: /cvs/src/sys/arch/hp300/hp300/clock.c,v
retrieving revision 1.2
diff -c -r1.2 clock.c
*** clock.c	1999/02/20 03:28:22	1.2
--- clock.c	1999/11/19 02:49:03
***************
*** 470,479 ****
  	decimal_to_bbc(9, 10, tmptr->tm_mon);
  	decimal_to_bbc(11, 12, tmptr->tm_year);
  
! 	/* Some bogusness to deal with seemingly broken hardware. Nonsense */
  	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
  
! 	write_bbc_reg(15, 13);	/* reset prescalar */
  
  	for (i = 0; i <= NUM_BBC_REGS; i++)
  	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
--- 470,479 ----
  	decimal_to_bbc(9, 10, tmptr->tm_mon);
  	decimal_to_bbc(11, 12, tmptr->tm_year);
  
! 	/* Set MSB of reg 5 to specify 24 hour mode */
  	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
  
! 	write_bbc_reg(15, 13);	/* reset prescaler */
  
  	for (i = 0; i <= NUM_BBC_REGS; i++)
  	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
***************
*** 499,507 ****
  	rt.tm_sec  = (hms % 3600) % 60;
  
  	/* Number of years in days */
  	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
  	  	day -= days_in_year(i);
! 	rt.tm_year = i;
  	
  	/* Number of months in days left */
  	if (leapyear(rt.tm_year))
--- 499,514 ----
  	rt.tm_sec  = (hms % 3600) % 60;
  
  	/* Number of years in days */
+ 	/*
+ 	 * Note that although one can successfully write any 4 bit
+ 	 * number including 0xa..0xf into reg 12 of the bbc, it
+ 	 * will not roll from 0x9 to 0xa.  Better to just treat
+ 	 * anything greater than 9 as invalid, so disallow year > 100
+ 	 * and compensate when reading out in bbc_to_gmt().
+ 	 */
  	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
  	  	day -= days_in_year(i);
! 	rt.tm_year = (i < 100) ? i : (i - 100);
  	
  	/* Number of months in days left */
  	if (leapyear(rt.tm_year))
***************
*** 531,547 ****
  	min = bbc_to_decimal(3, 2);
  
  	/*
! 	 * Hours are different for some reason. Makes no sense really.
  	 */
  	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
  	day   = bbc_to_decimal(8, 7);
  	month = bbc_to_decimal(10, 9);
  	year  = bbc_to_decimal(12, 11) + 1900;
  
  	range_test(hour, 0, 23);
  	range_test(day, 1, 31);
  	range_test(month, 1, 12);
! 	range_test(year, STARTOFTIME, 2000);
  
  	tmp = 0;
  
--- 538,556 ----
  	min = bbc_to_decimal(3, 2);
  
  	/*
! 	 * Upper two bits of reg 5 specify 12/24 hr mode and AM/PM.
  	 */
  	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
  	day   = bbc_to_decimal(8, 7);
  	month = bbc_to_decimal(10, 9);
  	year  = bbc_to_decimal(12, 11) + 1900;
+ 	if (year < STARTOFTIME)
+ 		year += 100;
  
  	range_test(hour, 0, 23);
  	range_test(day, 1, 31);
  	range_test(month, 1, 12);
! 	range_test(year, STARTOFTIME, 2037);
  
  	tmp = 0;
  
>Audit-Trail:
>Unformatted: