⬢ DragonFlyBSD Kernel Audit
DF-5623 / fix.diff
← back to finding ↓ download raw
--- a/sys/platform/pc64/x86_64/cpufreq_machdep.c
+++ b/sys/platform/pc64/x86_64/cpufreq_machdep.c
@@ -50,10 +50,12 @@
 	wrmsr(AMD0F_MSR_FIDVID_CTL, \
 	(((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
 
-#define AMD0F_WAIT_FIDVID_CHG(status) \
-do { \
-	(status) = rdmsr(AMD0F_MSR_FIDVID_STATUS); \
-} while (AMD0F_STA_PENDING(status))
+#define AMD0F_WAIT_FIDVID_CHG(status) do {					\
+	int _chg_n = 80000000;						\
+	do {								\
+		(status) = rdmsr(AMD0F_MSR_FIDVID_STATUS);	\
+	} while (AMD0F_STA_PENDING(status) && --_chg_n > 0);	\
+} while (0)
 
 #define AMD0F_FID2VCO(fid) \
 	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
@@ -68,7 +70,7 @@
 amd0f_set_fidvid(const struct amd0f_fidvid *fv, const struct amd0f_xsit *xsit)
 {
 	uint32_t val, cfid, cvid;
-	int rvo;
+	int rvo, nloop;
 	uint64_t status;
 
 	/*
@@ -100,6 +102,8 @@
 			val = 0;
 		AMD0F_WRITE_FIDVID(cfid, val, 0ULL);
 		AMD0F_WAIT_FIDVID_CHG(status);
+		if (AMD0F_STA_PENDING(status))
+			return ETIMEDOUT;
 		cvid = AMD0F_STA_CVID(status);
 		AMD0F_DELAY_VST(xsit->vst);
 	}
@@ -110,6 +114,8 @@
 		 * under Linux */
 		AMD0F_WRITE_FIDVID(cfid, cvid - 1, 0ULL);
 		AMD0F_WAIT_FIDVID_CHG(status);
+		if (AMD0F_STA_PENDING(status))
+			return ETIMEDOUT;
 		cvid = AMD0F_STA_CVID(status);
 		AMD0F_DELAY_VST(xsit->vst);
 	}
@@ -123,7 +129,9 @@
 
 		vco_fid = AMD0F_FID2VCO(fv->fid);
 		vco_cfid = AMD0F_FID2VCO(cfid);
-		while (abs(vco_fid - vco_cfid) > 2) {
+		for (nloop = 0; abs(vco_fid - vco_cfid) > 2; ++nloop) {
+			if (nloop >= 64)
+				return ETIMEDOUT;
 			if (fv->fid > cfid) {
 				if (cfid > 6)
 					val = cfid + 2;
@@ -136,6 +144,8 @@
 			AMD0F_WRITE_FIDVID(val, cvid,
 				(uint64_t)xsit->pll_time * 1000 / 5);
 			AMD0F_WAIT_FIDVID_CHG(status);
+			if (AMD0F_STA_PENDING(status))
+				return ETIMEDOUT;
 			cfid = AMD0F_STA_CFID(status);
 			AMD0F_DELAY_IRT(xsit->irt);
 			vco_cfid = AMD0F_FID2VCO(cfid);
@@ -144,6 +154,8 @@
 			AMD0F_WRITE_FIDVID(fv->fid, cvid,
 				(uint64_t)xsit->pll_time * 1000 / 5);
 			AMD0F_WAIT_FIDVID_CHG(status);
+			if (AMD0F_STA_PENDING(status))
+				return ETIMEDOUT;
 			cfid = AMD0F_STA_CFID(status);
 			AMD0F_DELAY_IRT(xsit->irt);
 		}
@@ -155,9 +167,15 @@
 	if (cvid != fv->vid) {
 		AMD0F_WRITE_FIDVID(cfid, fv->vid, 0ULL);
 		AMD0F_WAIT_FIDVID_CHG(status);
+		if (AMD0F_STA_PENDING(status))
+			return ETIMEDOUT;
 		cvid = AMD0F_STA_CVID(status);
 		AMD0F_DELAY_VST(xsit->vst);
 	}
+	if (cfid != fv->fid || cvid != fv->vid)
+		kprintf("amd0f: FID/VID transition did not converge "
+			"(fid %u/%u, vid %u/%u)\n",
+			cfid, fv->fid, cvid, fv->vid);
 	return 0;
 }