From: Peter H. Froehlich Date: Thu, 5 Nov 2015 21:02:23 +0000 (-0500) Subject: Disentangled CMOS from LAPIC. X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=b63ef4a00bb8ba5ea715df963d6989a45118eb24;p=xv6-public Disentangled CMOS from LAPIC. --- diff --git a/Makefile b/Makefile index 4a2d7ff..ca7b586 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ OBJS = \ bio.o\ + cmos.o\ console.o\ debug.o\ exec.o\ diff --git a/cmos.c b/cmos.c new file mode 100644 index 0000000..6036031 --- /dev/null +++ b/cmos.c @@ -0,0 +1,73 @@ +#include "types.h" +#include "defs.h" +#include "date.h" +#include "x86.h" +#include "cmos.h" + +uint cmosread(uint reg) +{ + if(reg >= CMOS_NMI_BIT) + panic("cmosread: invalid register"); + + outb(CMOS_PORT, reg); + microdelay(200); + return inb(CMOS_RETURN); +} + +void cmoswrite(uint reg, uint data) +{ + if(reg >= CMOS_NMI_BIT) + panic("cmoswrite: invalid register"); + if(data > 0xff) + panic("cmoswrite: invalid data"); + + outb(CMOS_PORT, reg); + microdelay(200); + outb(CMOS_RETURN, data); +} + +static void fill_rtcdate(struct rtcdate *r) +{ + r->second = cmosread(CMOS_SECS); + r->minute = cmosread(CMOS_MINS); + r->hour = cmosread(CMOS_HOURS); + r->day = cmosread(CMOS_DAY); + r->month = cmosread(CMOS_MONTH); + r->year = cmosread(CMOS_YEAR); +} + +// qemu seems to use 24-hour GWT and the values are BCD encoded +void cmostime(struct rtcdate *r) +{ + struct rtcdate t1, t2; + int sb, bcd; + + sb = cmosread(CMOS_STATB); + + bcd = (sb & CMOS_BINARY_BIT) == 0; + + // make sure CMOS doesn't modify time while we read it + for (;;) { + fill_rtcdate(&t1); + if (cmosread(CMOS_STATA) & CMOS_UIP_BIT) + continue; + fill_rtcdate(&t2); + if (memcmp(&t1, &t2, sizeof(t1)) == 0) + break; + } + + // convert + if (bcd) { +#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf)) + CONV(second); + CONV(minute); + CONV(hour ); + CONV(day ); + CONV(month ); + CONV(year ); +#undef CONV + } + + *r = t1; + r->year += 2000; +} diff --git a/cmos.h b/cmos.h new file mode 100644 index 0000000..378f152 --- /dev/null +++ b/cmos.h @@ -0,0 +1,28 @@ +// Access is through these two I/O addresses. Select a register by writing to +// CMOS_PORT, then read/write data from/to that register through CMOS_RETURN. +// +// Note that due to general IBM insanity bit 7 of CMOS_PORT controls whether +// NMIs get to "the" CPU or not. All of xv6 has ignored this issue for years, +// so we do the same. But this technically makes CMOS_PORT a 7-bit address +// and the CMOS cannot have more than 128 registers. The original Motorola +// MC146818 in fact had 64 registers (only AD0-AD5 were used for addresses). +#define CMOS_PORT 0x70 + #define CMOS_NMI_BIT (1 << 7) // NMI enabled +#define CMOS_RETURN 0x71 + +// The CMOS registers for the real-time clock. +#define CMOS_SECS 0x00 +#define CMOS_MINS 0x02 +#define CMOS_HOURS 0x04 +#define CMOS_DAY 0x07 +#define CMOS_MONTH 0x08 +#define CMOS_YEAR 0x09 +#define CMOS_STATA 0x0a + #define CMOS_UIP_BIT (1 << 7) // RTC update in progress +#define CMOS_STATB 0x0b + #define CMOS_24H_BIT (1 << 1) // RTC 24/12 hour format + #define CMOS_BINARY_BIT (1 << 2) // RTC binary/bcd format + +// The shutdown status register is used in lapic.c to bring up APs. +#define CMOS_SHUTDOWN_STAT 0x0f + #define CMOS_JMP_DWORD_NO_EOI 0x0a diff --git a/defs.h b/defs.h index b0406df..e43788e 100644 --- a/defs.h +++ b/defs.h @@ -16,6 +16,11 @@ struct buf* bread(uint, uint); void brelse(struct buf*); void bwrite(struct buf*); +// cmos.c +uint cmosread(uint reg); +void cmoswrite(uint reg, uint data); +void cmostime(struct rtcdate *r); + // console.c void consoleinit(void); void cprintf(char*, ...); @@ -78,7 +83,6 @@ void kinit2(void*, void*); void kbdintr(void); // lapic.c -void cmostime(struct rtcdate *r); int cpunum(void); extern volatile uint* lapic; void lapiceoi(void); diff --git a/lapic.c b/lapic.c index 4da4214..230c2de 100644 --- a/lapic.c +++ b/lapic.c @@ -3,11 +3,11 @@ #include "types.h" #include "defs.h" -#include "date.h" #include "memlayout.h" #include "traps.h" #include "mmu.h" #include "x86.h" +#include "cmos.h" // Local APIC registers, divided by 4 for use as uint[] indices. #define ID (0x0020/4) // ID @@ -53,19 +53,19 @@ lapicw(int index, int value) void lapicinit(void) { - if(!lapic) + if(!lapic) return; // Enable local APIC; set spurious interrupt vector. lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS)); // The timer repeatedly counts down at bus frequency - // from lapic[TICR] and then issues an interrupt. + // from lapic[TICR] and then issues an interrupt. // If xv6 cared more about precise timekeeping, // TICR would be calibrated using an external time source. lapicw(TDCR, X1); lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER)); - lapicw(TICR, 10000000); + lapicw(TICR, 10000000); // Disable logical interrupt lines. lapicw(LINT0, MASKED); @@ -131,9 +131,6 @@ microdelay(int us) { } -#define CMOS_PORT 0x70 -#define CMOS_RETURN 0x71 - // Start additional processor running entry code at addr. // See Appendix B of MultiProcessor Specification. void @@ -141,12 +138,11 @@ lapicstartap(uchar apicid, uint addr) { int i; ushort *wrv; - + // "The BSP must initialize CMOS shutdown code to 0AH // and the warm reset vector (DWORD based at 40:67) to point at // the AP startup code prior to the [universal startup algorithm]." - outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code - outb(CMOS_PORT+1, 0x0A); + cmoswrite(CMOS_SHUTDOWN_STAT, CMOS_JMP_DWORD_NO_EOI); wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector wrv[0] = 0; wrv[1] = addr >> 4; @@ -158,7 +154,7 @@ lapicstartap(uchar apicid, uint addr) microdelay(200); lapicw(ICRLO, INIT | LEVEL); microdelay(100); // should be 10ms, but too slow in Bochs! - + // Send startup IPI (twice!) to enter code. // Regular hardware is supposed to only accept a STARTUP // when it is in the halted state due to an INIT. So the second @@ -171,67 +167,3 @@ lapicstartap(uchar apicid, uint addr) } } -#define CMOS_STATA 0x0a -#define CMOS_STATB 0x0b -#define CMOS_UIP (1 << 7) // RTC update in progress - -#define SECS 0x00 -#define MINS 0x02 -#define HOURS 0x04 -#define DAY 0x07 -#define MONTH 0x08 -#define YEAR 0x09 - -static uint cmos_read(uint reg) -{ - outb(CMOS_PORT, reg); - microdelay(200); - - return inb(CMOS_RETURN); -} - -static void fill_rtcdate(struct rtcdate *r) -{ - r->second = cmos_read(SECS); - r->minute = cmos_read(MINS); - r->hour = cmos_read(HOURS); - r->day = cmos_read(DAY); - r->month = cmos_read(MONTH); - r->year = cmos_read(YEAR); -} - -// qemu seems to use 24-hour GWT and the values are BCD encoded -void cmostime(struct rtcdate *r) -{ - struct rtcdate t1, t2; - int sb, bcd; - - sb = cmos_read(CMOS_STATB); - - bcd = (sb & (1 << 2)) == 0; - - // make sure CMOS doesn't modify time while we read it - for (;;) { - fill_rtcdate(&t1); - if (cmos_read(CMOS_STATA) & CMOS_UIP) - continue; - fill_rtcdate(&t2); - if (memcmp(&t1, &t2, sizeof(t1)) == 0) - break; - } - - // convert - if (bcd) { -#define CONV(x) (t1.x = ((t1.x >> 4) * 10) + (t1.x & 0xf)) - CONV(second); - CONV(minute); - CONV(hour ); - CONV(day ); - CONV(month ); - CONV(year ); -#undef CONV - } - - *r = t1; - r->year += 2000; -}