diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99047e58..8ce3ffc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -384,6 +384,13 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_MOCK_TESTS)
     TARGET_COMPILE_DEFINITIONS(cpuinfo_mock PRIVATE _GNU_SOURCE=1)
   ENDIF()
 
+  IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv(32|64))$")
+    ADD_EXECUTABLE(starfive-visionfive-v2-test test/mock/starfive-visionfive-v2.cc)
+    TARGET_INCLUDE_DIRECTORIES(starfive-visionfive-v2-test BEFORE PRIVATE test/mock)
+    TARGET_LINK_LIBRARIES(starfive-visionfive-v2-test PRIVATE cpuinfo_mock gtest)
+    ADD_TEST(NAME starfive-visionfive-v2-test COMMAND starfive-visionfive-v2-test)
+  ENDIF()
+
   IF(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv5te|armv7-a)$")
     ADD_EXECUTABLE(atm7029b-tablet-test test/mock/atm7029b-tablet.cc)
     TARGET_INCLUDE_DIRECTORIES(atm7029b-tablet-test BEFORE PRIVATE test/mock)
diff --git a/include/cpuinfo-mock.h b/include/cpuinfo-mock.h
index 3c1f637d..06741716 100644
--- a/include/cpuinfo-mock.h
+++ b/include/cpuinfo-mock.h
@@ -58,12 +58,22 @@ struct cpuinfo_mock_property {
 	int CPUINFO_ABI cpuinfo_mock_close(int fd);
 	ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity);
 
-	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
+	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \
+		|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
 		void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap);
 	#endif
 	#if CPUINFO_ARCH_ARM
 		void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2);
 	#endif
+	#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
+		#include <sys/hwprobe.h>
+		typedef int (*cpuinfo_mock_riscv_hwprobe)(struct riscv_hwprobe* pairs,
+							  size_t pair_count,
+							  size_t cpu_count,
+							  unsigned long *cpus,
+							  unsigned int flags);
+		void CPUINFO_ABI cpuinfo_set_riscv_hwprobe(cpuinfo_mock_riscv_hwprobe riscv_hwprobe);
+	#endif
 #endif
 
 #if defined(__ANDROID__)
diff --git a/src/riscv/linux/riscv-hw.c b/src/riscv/linux/riscv-hw.c
index befdf3f6..4afc1082 100644
--- a/src/riscv/linux/riscv-hw.c
+++ b/src/riscv/linux/riscv-hw.c
@@ -5,6 +5,17 @@
 #include <riscv/api.h>
 #include <riscv/linux/api.h>
 
+#if CPUINFO_MOCK
+#include <cpuinfo-mock.h>
+#endif  // CPUINFO_MOCK
+
+#if CPUINFO_MOCK
+static cpuinfo_mock_riscv_hwprobe g_riscv_hwprobe;
+void cpuinfo_set_riscv_hwprobe(cpuinfo_mock_riscv_hwprobe riscv_hwprobe) {
+  g_riscv_hwprobe = riscv_hwprobe;
+}
+#endif  // CPUINFO_MOCK
+
 void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
 		uint32_t processor,
 		enum cpuinfo_vendor vendor[restrict static 1],
@@ -33,9 +44,13 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
 	CPU_SET_S(processor, cpu_set_size, cpu_set);
 
 	/* Request all available information from hwprobe. */
-	int ret = __riscv_hwprobe(pairs, pairs_count,
-                                  cpu_set_size, (unsigned long*)cpu_set,
-                                  0 /* flags */);
+#if CPUINFO_MOCK
+	int ret = g_riscv_hwprobe(pairs, pairs_count, cpu_set_size,
+				  (unsigned long*)cpu_set, 0 /* flags */);
+#else
+	int ret = __riscv_hwprobe(pairs, pairs_count, cpu_set_size,
+				  (unsigned long*)cpu_set, 0 /* flags */);
+#endif
 	if (ret < 0) {
 		cpuinfo_log_warning("failed to get hwprobe information, err: %d", ret);
 		goto cleanup;
diff --git a/src/riscv/linux/riscv-isa.c b/src/riscv/linux/riscv-isa.c
index ace451b8..112e705c 100644
--- a/src/riscv/linux/riscv-isa.c
+++ b/src/riscv/linux/riscv-isa.c
@@ -3,6 +3,10 @@
 
 #include <riscv/linux/api.h>
 
+#if CPUINFO_MOCK
+#include <cpuinfo-mock.h>
+#endif  // CPUINFO_MOCK
+
 /**
  * arch/riscv/include/uapi/asm/hwcap.h
  *
@@ -16,9 +20,20 @@
 #define COMPAT_HWCAP_ISA_C	(1 << ('C' - 'A'))
 #define COMPAT_HWCAP_ISA_V	(1 << ('V' - 'A'))
 
+#if CPUINFO_MOCK
+static uint32_t g_mock_hwcap = 0;
+void cpuinfo_set_hwcap(uint32_t hwcap) {
+    g_mock_hwcap = hwcap;
+}
+#endif  // CPUINFO_MOCK
+
 void cpuinfo_riscv_linux_decode_isa_from_hwcap(
 		struct cpuinfo_riscv_isa isa[restrict static 1]) {
-	const unsigned long hwcap = getauxval(AT_HWCAP);
+#if CPUINFO_MOCK
+	const uint32_t hwcap = g_mock_hwcap;
+#else
+	const uint32_t hwcap = getauxval(AT_HWCAP);
+#endif  // CPUINFO_MOCK
 
 	if (hwcap & COMPAT_HWCAP_ISA_I) {
 		isa->i = true;
diff --git a/test/mock/starfive-visionfive-v2.cc b/test/mock/starfive-visionfive-v2.cc
new file mode 100644
index 00000000..a4dcb568
--- /dev/null
+++ b/test/mock/starfive-visionfive-v2.cc
@@ -0,0 +1,276 @@
+#include <gtest/gtest.h>
+
+#include <cpuinfo.h>
+#include <cpuinfo-mock.h>
+
+
+TEST(PROCESSORS, count) {
+	ASSERT_EQ(4, cpuinfo_get_processors_count());
+}
+
+TEST(PROCESSORS, non_null) {
+	ASSERT_TRUE(cpuinfo_get_processors());
+}
+
+TEST(PROCESSORS, core) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_core(i), cpuinfo_get_processor(i)->core);
+	}
+}
+
+TEST(PROCESSORS, cluster) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_cluster(0), cpuinfo_get_processor(i)->cluster);
+	}
+}
+
+TEST(PROCESSORS, package) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_package(0), cpuinfo_get_processor(i)->package);
+	}
+}
+
+TEST(PROCESSORS, linux_id) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(i, cpuinfo_get_processor(i)->linux_id);
+	}
+}
+
+TEST(PROCESSORS, l1i) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_l1i_cache(i), cpuinfo_get_processor(i)->cache.l1i);
+	}
+}
+
+TEST(PROCESSORS, l1d) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_l1d_cache(i), cpuinfo_get_processor(i)->cache.l1d);
+	}
+}
+
+TEST(PROCESSORS, l2) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_l2_cache(0), cpuinfo_get_processor(i)->cache.l2);
+	}
+}
+
+TEST(PROCESSORS, l3) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_FALSE(cpuinfo_get_processor(i)->cache.l3);
+	}
+}
+
+TEST(PROCESSORS, l4) {
+	for (uint32_t i = 0; i < cpuinfo_get_processors_count(); i++) {
+		ASSERT_FALSE(cpuinfo_get_processor(i)->cache.l4);
+	}
+}
+
+TEST(CORES, count) {
+	ASSERT_EQ(4, cpuinfo_get_cores_count());
+}
+
+TEST(CORES, non_null) {
+	ASSERT_TRUE(cpuinfo_get_cores());
+}
+
+TEST(CORES, processor_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(i, cpuinfo_get_core(i)->processor_start);
+	}
+}
+
+TEST(CORES, processor_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(1, cpuinfo_get_core(i)->processor_count);
+	}
+}
+
+TEST(CORES, core_id) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(i, cpuinfo_get_core(i)->core_id);
+	}
+}
+
+TEST(CORES, cluster) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_cluster(0), cpuinfo_get_core(i)->cluster);
+	}
+}
+
+TEST(CORES, package) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_package(0), cpuinfo_get_core(i)->package);
+	}
+}
+
+TEST(CORES, vendor) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(cpuinfo_vendor_sifive, cpuinfo_get_core(i)->vendor);
+	}
+}
+
+TEST(CORES, uarch) {
+	for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) {
+		ASSERT_EQ(cpuinfo_uarch_unknown, cpuinfo_get_core(i)->uarch);
+	}
+}
+
+TEST(CLUSTERS, count) {
+	ASSERT_EQ(1, cpuinfo_get_clusters_count());
+}
+
+TEST(CLUSTERS, non_null) {
+	ASSERT_TRUE(cpuinfo_get_clusters());
+}
+
+TEST(CLUSTERS, processor_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(0, cpuinfo_get_cluster(i)->processor_start);
+	}
+}
+
+TEST(CLUSTERS, processor_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(4, cpuinfo_get_cluster(i)->processor_count);
+	}
+}
+
+TEST(CLUSTERS, core_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(0, cpuinfo_get_cluster(i)->core_start);
+	}
+}
+
+TEST(CLUSTERS, core_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(4, cpuinfo_get_cluster(i)->core_count);
+	}
+}
+
+TEST(CLUSTERS, cluster_id) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(i, cpuinfo_get_cluster(i)->cluster_id);
+	}
+}
+
+TEST(CLUSTERS, package) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(cpuinfo_get_package(0), cpuinfo_get_cluster(i)->package);
+	}
+}
+
+TEST(CLUSTERS, vendor) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(cpuinfo_vendor_sifive, cpuinfo_get_cluster(i)->vendor);
+	}
+}
+
+TEST(CLUSTERS, uarch) {
+	for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) {
+		ASSERT_EQ(cpuinfo_uarch_unknown, cpuinfo_get_cluster(i)->uarch);
+	}
+}
+
+TEST(PACKAGES, count) {
+	ASSERT_EQ(1, cpuinfo_get_packages_count());
+}
+
+TEST(PACKAGES, name) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ("",
+			std::string(cpuinfo_get_package(i)->name,
+				strnlen(cpuinfo_get_package(i)->name, CPUINFO_PACKAGE_NAME_MAX)));
+	}
+}
+
+TEST(PACKAGES, processor_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(0, cpuinfo_get_package(i)->processor_start);
+	}
+}
+
+TEST(PACKAGES, processor_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(4, cpuinfo_get_package(i)->processor_count);
+	}
+}
+
+TEST(PACKAGES, core_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(0, cpuinfo_get_package(i)->core_start);
+	}
+}
+
+TEST(PACKAGES, core_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(4, cpuinfo_get_package(i)->core_count);
+	}
+}
+
+TEST(PACKAGES, cluster_start) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(0, cpuinfo_get_package(i)->cluster_start);
+	}
+}
+
+TEST(PACKAGES, cluster_count) {
+	for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) {
+		ASSERT_EQ(1, cpuinfo_get_package(i)->cluster_count);
+	}
+}
+
+TEST(ISA, i) {
+	ASSERT_TRUE(cpuinfo_isa.i);
+}
+
+TEST(ISA, m) {
+	ASSERT_TRUE(cpuinfo_isa.m);
+}
+
+TEST(ISA, a) {
+	ASSERT_TRUE(cpuinfo_isa.a);
+}
+
+TEST(ISA, f) {
+	ASSERT_TRUE(cpuinfo_isa.f);
+}
+
+TEST(ISA, d) {
+	ASSERT_TRUE(cpuinfo_isa.d);
+}
+
+TEST(ISA, c) {
+	ASSERT_TRUE(cpuinfo_isa.c);
+}
+
+TEST(ISA, v) {
+	ASSERT_FALSE(cpuinfo_isa.v);
+}
+
+#include <starfive-visionfive-v2.h>
+
+int riscv_hwprobe(struct riscv_hwprobe* pairs, size_t pair_count,
+                  size_t /* cpu_count */, unsigned long * /* cpus */,
+                  unsigned int /* flags */) {
+	for (size_t pair = 0; pair < pair_count; pair++) {
+		switch (pairs[pair].key) {
+		case RISCV_HWPROBE_KEY_MVENDORID:
+			pairs[pair].value = 0x489;
+			break;
+		default:
+			pairs[pair].key = -1;
+			break;
+		}
+	}
+	return 0;
+}
+
+int main(int argc, char* argv[]) {
+	cpuinfo_mock_filesystem(filesystem);
+	cpuinfo_set_hwcap(UINT32_C(0x0000112D));
+	cpuinfo_set_riscv_hwprobe(&riscv_hwprobe);
+	cpuinfo_initialize();
+	::testing::InitGoogleTest(&argc, argv);
+	return RUN_ALL_TESTS();
+}
diff --git a/test/mock/starfive-visionfive-v2.h b/test/mock/starfive-visionfive-v2.h
new file mode 100644
index 00000000..1602de21
--- /dev/null
+++ b/test/mock/starfive-visionfive-v2.h
@@ -0,0 +1,187 @@
+struct cpuinfo_mock_file filesystem[] = {
+	{
+		.path = "/proc/cpuinfo",
+		.size = 292,
+		.content =
+			"processor\t: 0\n"
+			"hart\t: 1\n"
+			"isa\t: rv64imafdc\n"
+			"mmu\t: sv39\n"
+			"uarch\t: sifive,u74-mc\n"
+			"\n"
+			"processor\t: 1\n"
+			"hart\t: 2\n"
+			"isa\t: rv64imafdc\n"
+			"mmu\t: sv39\n"
+			"uarch\t: sifive,u74-mc\n"
+			"\n"
+			"processor\t: 2\n"
+			"hart\t: 3\n"
+			"isa\t: rv64imafdc\n"
+			"mmu\t: sv39\n"
+			"uarch\t: sifive,u74-mc\n"
+			"\n"
+			"processor\t: 3\n"
+			"hart\t: 4\n"
+			"isa\t: rv64imafdc\n"
+			"mmu\t: sv39\n"
+			"uarch\t: sifive,u74-mc\n"
+			"\n"
+	},
+	{
+		.path = "/sys/devices/system/cpu/kernel_max",
+		.size = 3,
+		.content = "31\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/possible",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/present",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/physical_package_id",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/core_cpus_list",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/core_siblings_list",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/cluster_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/package_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/core_id",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu0/topology/thread_siblings_list",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/physical_package_id",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/core_cpus_list",
+		.size = 2,
+		.content = "1\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/core_siblings_list",
+		.size = 2,
+		.content = "1\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/cluster_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/package_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/core_id",
+		.size = 2,
+		.content = "1\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu1/topology/thread_siblings_list",
+		.size = 2,
+		.content = "1\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/physical_package_id",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/core_cpus_list",
+		.size = 2,
+		.content = "2\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/core_siblings_list",
+		.size = 2,
+		.content = "2\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/cluster_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/package_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/core_id",
+		.size = 2,
+		.content = "2\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu2/topology/thread_siblings_list",
+		.size = 2,
+		.content = "2\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/physical_package_id",
+		.size = 2,
+		.content = "0\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/core_cpus_list",
+		.size = 2,
+		.content = "3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/core_siblings_list",
+		.size = 2,
+		.content = "3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/cluster_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/package_cpus_list",
+		.size = 4,
+		.content = "0-3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/core_id",
+		.size = 2,
+		.content = "3\n",
+	},
+	{
+		.path = "/sys/devices/system/cpu/cpu3/topology/thread_siblings_list",
+		.size = 2,
+		.content = "3\n",
+	},
+	{ NULL },
+};