arch/x86/smbios: Populate SMBIOS type 7 with cache information

SMBIOS has a field to display the cache size, which is currently
set to UNKNOWN unconditionally, multiply the cache size of L1 and L2
by the number of cores.

TEST=Execute "dmidecode -t 7" to check if the cache information
is correct for Deltalake platform

Change-Id: Ieeb5d3346454ffb2291613dc2aa24b31d10c2e04
Signed-off-by: Morgan Jang <Morgan_Jang@wiwynn.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/46068
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Jonathan Zhang <jonzhang@fb.com>
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c
index 2995ece..19b6f5d 100644
--- a/src/arch/x86/smbios.c
+++ b/src/arch/x86/smbios.c
@@ -478,6 +478,51 @@
 	return (res.eax > 0) ? 0x0c : 0x6;
 }
 
+unsigned int __weak smbios_cache_error_correction_type(u8 level)
+{
+	return SMBIOS_CACHE_ERROR_CORRECTION_UNKNOWN;
+}
+
+unsigned int __weak smbios_cache_sram_type(void)
+{
+	return  SMBIOS_CACHE_SRAM_TYPE_UNKNOWN;
+}
+
+unsigned int __weak smbios_cache_conf_operation_mode(u8 level)
+{
+	return SMBIOS_CACHE_OP_MODE_UNKNOWN; /* Unknown */
+}
+
+static size_t get_number_of_caches(struct cpuid_result res_deterministic_cache)
+{
+	size_t max_logical_cpus_sharing_cache = 0;
+	size_t number_of_cpus_per_package = 0;
+	size_t max_logical_cpus_per_package = 0;
+	struct cpuid_result res;
+
+	if (!cpu_have_cpuid())
+		return 1;
+
+	res = cpuid(1);
+
+	max_logical_cpus_per_package = (res.ebx >> 16) & 0xff;
+
+	max_logical_cpus_sharing_cache  = ((res_deterministic_cache.eax >> 14) & 0xfff) + 1;
+
+	/* Check if it's last level cache */
+	if (max_logical_cpus_sharing_cache == max_logical_cpus_per_package)
+		return 1;
+
+	if (cpuid_get_max_func() >= 0xb) {
+		res = cpuid_ext(0xb, 1);
+		number_of_cpus_per_package = res.ebx & 0xff;
+	} else {
+		number_of_cpus_per_package = max_logical_cpus_per_package;
+	}
+
+	return number_of_cpus_per_package / max_logical_cpus_sharing_cache;
+}
+
 static int smbios_write_type1(unsigned long *current, int handle)
 {
 	struct smbios_type1 *t = (struct smbios_type1 *)*current;
@@ -662,7 +707,6 @@
 {
 	struct smbios_type7 *t = (struct smbios_type7 *)*current;
 	int len = sizeof(struct smbios_type7);
-	static unsigned int cnt = 0;
 	char buf[8];
 
 	memset(t, 0, sizeof(struct smbios_type7));
@@ -670,13 +714,13 @@
 	t->handle = handle;
 	t->length = len - 2;
 
-	snprintf(buf, sizeof(buf), "CACHE%x", cnt++);
+	snprintf(buf, sizeof(buf), "CACHE%x", level);
 	t->socket_designation = smbios_add_string(t->eos, buf);
 
 	t->cache_configuration = SMBIOS_CACHE_CONF_LEVEL(level) |
 		SMBIOS_CACHE_CONF_LOCATION(0) | /* Internal */
 		SMBIOS_CACHE_CONF_ENABLED(1) | /* Enabled */
-		SMBIOS_CACHE_CONF_OPERATION_MODE(3); /* Unknown */
+		SMBIOS_CACHE_CONF_OPERATION_MODE(smbios_cache_conf_operation_mode(level));
 
 	if (max_cache_size < (SMBIOS_CACHE_SIZE_MASK * KiB)) {
 		t->max_cache_size = max_cache_size / KiB;
@@ -716,7 +760,7 @@
 	t->supported_sram_type = sram_type;
 	t->current_sram_type = sram_type;
 	t->cache_speed = 0; /* Unknown */
-	t->error_correction_type = SMBIOS_CACHE_ERROR_CORRECTION_UNKNOWN;
+	t->error_correction_type = smbios_cache_error_correction_type(level);
 	t->system_cache_type = type;
 
 	len = t->length + smbios_string_table_len(t->eos);
@@ -811,7 +855,8 @@
 		const size_t partitions = CPUID_CACHE_PHYS_LINE(res) + 1;
 		const size_t cache_line_size = CPUID_CACHE_COHER_LINE(res) + 1;
 		const size_t number_of_sets = CPUID_CACHE_NO_OF_SETS(res) + 1;
-		const size_t cache_size = assoc * partitions * cache_line_size * number_of_sets;
+		const size_t cache_size = assoc * partitions * cache_line_size * number_of_sets
+							* get_number_of_caches(res);
 
 		if (!cache_type)
 			/* No more caches in the system */
@@ -840,7 +885,7 @@
 		const int h = (*handle)++;
 
 		update_max(len, *max_struct_size, smbios_write_type7(current, h,
-			   level, SMBIOS_CACHE_SRAM_TYPE_UNKNOWN, associativity,
+			   level, smbios_cache_sram_type(), associativity,
 			   type, cache_size, cache_size));
 
 		if (type4) {
diff --git a/src/include/smbios.h b/src/include/smbios.h
index 8033d6c..6a19655 100644
--- a/src/include/smbios.h
+++ b/src/include/smbios.h
@@ -61,6 +61,10 @@
 struct cpuid_result;
 unsigned int smbios_processor_family(struct cpuid_result res);
 
+unsigned int smbios_cache_error_correction_type(u8 level);
+unsigned int smbios_cache_sram_type(void);
+unsigned int smbios_cache_conf_operation_mode(u8 level);
+
 /* Used by mainboard to add port information of type 8 */
 struct port_information;
 int smbios_write_type8(unsigned long *current, int *handle,
@@ -501,6 +505,13 @@
 #define SMBIOS_CACHE_SIZE2_UNIT_64KB		(1UL << 31)
 #define SMBIOS_CACHE_SIZE2_MASK			0x7fffffff
 
+/* define for cache operation mode */
+
+#define SMBIOS_CACHE_OP_MODE_WRITE_THROUGH 0
+#define SMBIOS_CACHE_OP_MODE_WRITE_BACK 1
+#define SMBIOS_CACHE_OP_MODE_VARIES_WITH_MEMORY_ADDRESS 2
+#define SMBIOS_CACHE_OP_MODE_UNKNOWN 3
+
 struct smbios_type7 {
 	u8 type;
 	u8 length;