commit 6d91c78f0c240e3c7d81e19e85507e0aec580d6f
Author: Zhaoming Luo <zhmingluo@163.com>
Date:   Mon Mar 24 12:25:51 2025 +0800

    Integrate HPET so the functions used for getting time can have a higher accuracy
    
    Integrate HPET so host_get_time, host_get_time64, and host_get_uptime64
    are more precise. The highest precision can be 10ns when this patch is
    applied.
    
    * i386/i386/apic.c: Implement the two high-precision clock interface functions
      added in this patch for i386.
    * i386/i386at/model_dep.c: Initialize HPET if APIC is defined
    * kern/mach_clock.c: Integrate the high-precision clocks to have the
      10ns precise time values.
    * kern/mach_clock.h: Add two new interface functions for accessing the
      high-precision clocks.
    
    Message-ID: <20250324042551.4752-2-zhmingluo@163.com>

diff --git a/i386/i386/apic.c b/i386/i386/apic.c
index 77d555b5..3852b9aa 100644
--- a/i386/i386/apic.c
+++ b/i386/i386/apic.c
@@ -479,3 +479,21 @@ hpet_mdelay(uint32_t ms)
     hpet_udelay(ms * 1000);
 }
 
+/* This function is called in clock_interrupt(), so it's possible to be called
+   when HPET is not available.  */
+uint32_t
+hpclock_read_counter(void)
+{
+    /* We assume the APIC machines have HPET.  */
+#ifdef APIC
+    return HPET32(HPET_COUNTER);
+#else
+    return 0;
+#endif
+}
+
+uint32_t
+hpclock_get_counter_period_nsec(void)
+{
+    return hpet_period_nsec;
+}
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index 30449c37..42dadeb8 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -223,7 +223,9 @@ void machine_init(void)
 	 */
 	gdt_descr_tmp.linear_base += apboot_addr;
 	apboot_jmp_offset += apboot_addr;
+#endif
 
+#ifdef APIC
 	/*
 	 * Initialize the HPET
 	 */
diff --git a/kern/mach_clock.c b/kern/mach_clock.c
index 5501b7b8..48f673a4 100644
--- a/kern/mach_clock.c
+++ b/kern/mach_clock.c
@@ -83,6 +83,15 @@ unsigned	tickadj = 500 / HZ;	/* can adjust 100 usecs per second */
 unsigned	bigadj = 1000000;	/* adjust 10*tickadj if adjustment
 					   > bigadj */
 
+/* A high-precision (hardware) clock is taken into account to increase the
+ * accuracy of the functions used for getting time (e.g. host_get_time64()).
+ * The counter of the clock is read once in every clock interrupt.  When any
+ * of the functions used for getting time is called, the counter is read again
+ * and the difference between these two read is multiplied by the counter
+ * period and added to the read value from time or uptime to get a more
+ * accurate time read.  */
+uint32_t	last_hpc_read = 0;
+
 /*
  *	This update protocol, with a check value, allows
  *		do {
@@ -128,7 +137,8 @@ MACRO_BEGIN								\
 		__sync_synchronize();					\
 		(time)->nanoseconds = mtime->time_value.nanoseconds;	\
 		__sync_synchronize();					\
-	} while ((time)->seconds != mtime->check_seconds64);	\
+	} while ((time)->seconds != mtime->check_seconds64);		\
+	time_value64_add_hpc(time);					\
 MACRO_END
 
 #define read_mapped_uptime(uptime)				    	\
@@ -139,6 +149,7 @@ MACRO_BEGIN								\
 		(uptime)->nanoseconds = mtime->uptime_value.nanoseconds;\
 		__sync_synchronize();					\
 	} while ((uptime)->seconds != mtime->check_upseconds64);	\
+	time_value64_add_hpc(uptime);					\
 MACRO_END
 
 def_simple_lock_irq_data(static,	timer_lock)	/* lock for ... */
@@ -292,6 +303,7 @@ void clock_interrupt(
 		}
 	    }
 	}
+	last_hpc_read = hpclock_read_counter();
 }
 
 /*
@@ -426,6 +438,26 @@ clock_boottime_update(const struct time_value64 *new_time)
 	time_value64_add(&clock_boottime_offset, &delta);
 }
 
+/*
+ * Add the time value since last clock interrupt in nanosecond.
+ */
+static void
+time_value64_add_hpc(time_value64_t *value)
+{
+	uint32_t now = hpclock_read_counter();
+	/* Time since last clock interrupt in nanosecond.  */
+	int64_t ns = (now - last_hpc_read) * hpclock_get_counter_period_nsec();
+
+	/* Limit the value of ns under the period of a clock interrupt.  */
+	if (ns >= tick * 1000)
+	    /* Let ns stuck at the end of the clock interrupt period when
+	       something bad happens.  */
+	    ns = (tick * 1000) - 1;
+
+	time_value64_add_nanos(value, ns);
+}
+
+
 /*
  * Record a timestamp in STAMP.  Records values in the boot-time clock
  * frame.
diff --git a/kern/mach_clock.h b/kern/mach_clock.h
index d4f04f5e..e83b638c 100644
--- a/kern/mach_clock.h
+++ b/kern/mach_clock.h
@@ -110,4 +110,8 @@ extern boolean_t untimeout(timer_func_t *fcn, const void *param);
 extern int timeopen(dev_t dev, int flag, io_req_t ior);
 extern void timeclose(dev_t dev, int flag);
 
+/* For high-precision clocks.  */
+extern uint32_t hpclock_read_counter(void);
+extern uint32_t hpclock_get_counter_period_nsec(void);
+
 #endif /* _KERN_MACH_CLOCK_H_ */
