On Wed, Jan 25, 2012 at 06:46:03PM +0100, Rudolf Marek wrote:
And here is the other part.
How about this patch instead?
-Kevin
commit 1f35810efe39e156fbd8df1b84888f6e092d5337 Author: Kevin O'Connor kevin@koconnor.net Date: Sun Jan 29 14:15:14 2012 -0500
Add TSC emulation layer for 386/486 CPUs.
Original patch from Rudolf Marek.
Signed-off-by: Rudolf Marek r.marek@assembler.cz Signed-off-by: Kevin O'Connor kevin@koconnor.net
diff --git a/src/biosvar.h b/src/biosvar.h index ad791ab..b6f7174 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -239,6 +239,10 @@ struct extended_bios_data_area_s {
u16 boot_sequence;
+ /* TSC emulation timekeepers */ + u64 tsc_8254; + int last_tsc_8254; + // Stack space available for code that needs it. u8 extra_stack[512] __aligned(8); } PACKED; diff --git a/src/clock.c b/src/clock.c index fcdc698..e8a48a1 100644 --- a/src/clock.c +++ b/src/clock.c @@ -48,6 +48,12 @@ #define PM_MODE5 (5<<1) #define PM_CNT_BINARY (0<<0) #define PM_CNT_BCD (1<<0) +#define PM_READ_COUNTER0 (1<<1) +#define PM_READ_COUNTER1 (1<<2) +#define PM_READ_COUNTER2 (1<<3) +#define PM_READ_STATUSVALUE (0<<4) +#define PM_READ_VALUE (1<<4) +#define PM_READ_STATUS (2<<4)
/**************************************************************** @@ -57,10 +63,23 @@ #define CALIBRATE_COUNT 0x800 // Approx 1.7ms
u32 cpu_khz VAR16VISIBLE; +u8 no_tsc VAR16VISIBLE;
static void calibrate_tsc(void) { + u32 eax, ebx, ecx, edx, cpuid_features = 0; + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax > 0) + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + + if (!(cpuid_features & CPUID_TSC)) { + SET_GLOBAL(no_tsc, 1); + SET_GLOBAL(cpu_khz, PIT_TICK_RATE / 1000); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + // Setup "timer2" u8 orig = inb(PORT_PS2_CTRLB); outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); @@ -89,10 +108,43 @@ calibrate_tsc(void) dprintf(1, "CPU Mhz=%u\n", hz / 1000000); }
+static u64 +emulate_tsc(void) +{ + int cnt, d; + u16 ebda_seg = get_ebda_seg(); + u64 ret; + /* read timer 0 current count */ + ret = GET_EBDA2(ebda_seg, tsc_8254); + /* readback mode has slightly shifted registers, works on all 8254, readback PIT0 latch */ + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8)); + d = GET_EBDA2(ebda_seg, last_tsc_8254) - cnt; + /* Determine the ticks count from last invocation of this function */ + ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d); + SET_EBDA2(ebda_seg, last_tsc_8254, cnt); + SET_EBDA2(ebda_seg, tsc_8254, ret); + return ret; +} + +static u64 +get_tsc(void) +{ + if (unlikely(GET_GLOBAL(no_tsc))) + return emulate_tsc(); + return rdtscll(); +} + +int +check_tsc(u64 end) +{ + return (s64)(get_tsc() - end) > 0; +} + static void tscdelay(u64 diff) { - u64 start = rdtscll(); + u64 start = get_tsc(); u64 end = start + diff; while (!check_tsc(end)) cpu_relax(); @@ -101,7 +153,7 @@ tscdelay(u64 diff) static void tscsleep(u64 diff) { - u64 start = rdtscll(); + u64 start = get_tsc(); u64 end = start + diff; while (!check_tsc(end)) yield(); @@ -132,13 +184,13 @@ u64 calc_future_tsc(u32 msecs) { u32 khz = GET_GLOBAL(cpu_khz); - return rdtscll() + ((u64)khz * msecs); + return get_tsc() + ((u64)khz * msecs); } u64 calc_future_tsc_usec(u32 usecs) { u32 khz = GET_GLOBAL(cpu_khz); - return rdtscll() + ((u64)(khz/1000) * usecs); + return get_tsc() + ((u64)(khz/1000) * usecs); }
diff --git a/src/util.h b/src/util.h index 2c5f7eb..70d3c4c 100644 --- a/src/util.h +++ b/src/util.h @@ -50,6 +50,7 @@ static inline void wbinvd(void) asm volatile("wbinvd": : :"memory"); }
+#define CPUID_TSC (1 << 4) #define CPUID_MSR (1 << 5) #define CPUID_APIC (1 << 9) #define CPUID_MTRR (1 << 12) @@ -326,9 +327,7 @@ void lpt_setup(void); // clock.c #define PIT_TICK_RATE 1193180 // Underlying HZ of PIT #define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer -static inline int check_tsc(u64 end) { - return (s64)(rdtscll() - end) > 0; -} +int check_tsc(u64 end); void timer_setup(void); void ndelay(u32 count); void udelay(u32 count);