1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2019, Intel Corporation.
5 * Heterogeneous Memory Attributes Table (HMAT) representation
7 * This program parses and reports the platform's HMAT tables, and registers
8 * the applicable attributes with the node's interfaces.
11 #include <linux/acpi.h>
12 #include <linux/bitops.h>
13 #include <linux/device.h>
14 #include <linux/init.h>
15 #include <linux/list.h>
16 #include <linux/node.h>
17 #include <linux/sysfs.h>
19 static __initdata u8 hmat_revision;
21 static __init const char *hmat_data_type(u8 type)
24 case ACPI_HMAT_ACCESS_LATENCY:
25 return "Access Latency";
26 case ACPI_HMAT_READ_LATENCY:
27 return "Read Latency";
28 case ACPI_HMAT_WRITE_LATENCY:
29 return "Write Latency";
30 case ACPI_HMAT_ACCESS_BANDWIDTH:
31 return "Access Bandwidth";
32 case ACPI_HMAT_READ_BANDWIDTH:
33 return "Read Bandwidth";
34 case ACPI_HMAT_WRITE_BANDWIDTH:
35 return "Write Bandwidth";
41 static __init const char *hmat_data_type_suffix(u8 type)
44 case ACPI_HMAT_ACCESS_LATENCY:
45 case ACPI_HMAT_READ_LATENCY:
46 case ACPI_HMAT_WRITE_LATENCY:
48 case ACPI_HMAT_ACCESS_BANDWIDTH:
49 case ACPI_HMAT_READ_BANDWIDTH:
50 case ACPI_HMAT_WRITE_BANDWIDTH:
57 static __init u32 hmat_normalize(u16 entry, u64 base, u8 type)
62 * Check for invalid and overflow values
64 if (entry == 0xffff || !entry)
66 else if (base > (UINT_MAX / (entry)))
70 * Divide by the base unit for version 1, convert latency from
71 * picosenonds to nanoseconds if revision 2.
74 if (hmat_revision == 1) {
77 value = DIV_ROUND_UP(value, 10);
78 } else if (hmat_revision == 2) {
80 case ACPI_HMAT_ACCESS_LATENCY:
81 case ACPI_HMAT_READ_LATENCY:
82 case ACPI_HMAT_WRITE_LATENCY:
83 value = DIV_ROUND_UP(value, 1000);
92 static __init int hmat_parse_locality(union acpi_subtable_headers *header,
93 const unsigned long end)
95 struct acpi_hmat_locality *hmat_loc = (void *)header;
96 unsigned int init, targ, total_size, ipds, tpds;
97 u32 *inits, *targs, value;
101 if (hmat_loc->header.length < sizeof(*hmat_loc)) {
102 pr_notice("HMAT: Unexpected locality header length: %d\n",
103 hmat_loc->header.length);
107 type = hmat_loc->data_type;
108 ipds = hmat_loc->number_of_initiator_Pds;
109 tpds = hmat_loc->number_of_target_Pds;
110 total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
111 sizeof(*inits) * ipds + sizeof(*targs) * tpds;
112 if (hmat_loc->header.length < total_size) {
113 pr_notice("HMAT: Unexpected locality header length:%d, minimum required:%d\n",
114 hmat_loc->header.length, total_size);
118 pr_info("HMAT: Locality: Flags:%02x Type:%s Initiator Domains:%d Target Domains:%d Base:%lld\n",
119 hmat_loc->flags, hmat_data_type(type), ipds, tpds,
120 hmat_loc->entry_base_unit);
122 inits = (u32 *)(hmat_loc + 1);
123 targs = inits + ipds;
124 entries = (u16 *)(targs + tpds);
125 for (init = 0; init < ipds; init++) {
126 for (targ = 0; targ < tpds; targ++) {
127 value = hmat_normalize(entries[init * tpds + targ],
128 hmat_loc->entry_base_unit,
130 pr_info(" Initiator-Target[%d-%d]:%d%s\n",
131 inits[init], targs[targ], value,
132 hmat_data_type_suffix(type));
139 static __init int hmat_parse_cache(union acpi_subtable_headers *header,
140 const unsigned long end)
142 struct acpi_hmat_cache *cache = (void *)header;
145 if (cache->header.length < sizeof(*cache)) {
146 pr_notice("HMAT: Unexpected cache header length: %d\n",
147 cache->header.length);
151 attrs = cache->cache_attributes;
152 pr_info("HMAT: Cache: Domain:%d Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
153 cache->memory_PD, cache->cache_size, attrs,
154 cache->number_of_SMBIOShandles);
159 static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
160 const unsigned long end)
162 struct acpi_hmat_proximity_domain *p = (void *)header;
164 if (p->header.length != sizeof(*p)) {
165 pr_notice("HMAT: Unexpected address range header length: %d\n",
170 if (hmat_revision == 1)
171 pr_info("HMAT: Memory (%#llx length %#llx) Flags:%04x Processor Domain:%d Memory Domain:%d\n",
172 p->reserved3, p->reserved4, p->flags, p->processor_PD,
175 pr_info("HMAT: Memory Flags:%04x Processor Domain:%d Memory Domain:%d\n",
176 p->flags, p->processor_PD, p->memory_PD);
181 static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
182 const unsigned long end)
184 struct acpi_hmat_structure *hdr = (void *)header;
190 case ACPI_HMAT_TYPE_ADDRESS_RANGE:
191 return hmat_parse_proximity_domain(header, end);
192 case ACPI_HMAT_TYPE_LOCALITY:
193 return hmat_parse_locality(header, end);
194 case ACPI_HMAT_TYPE_CACHE:
195 return hmat_parse_cache(header, end);
201 static __init int hmat_init(void)
203 struct acpi_table_header *tbl;
204 enum acpi_hmat_type i;
210 status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
211 if (ACPI_FAILURE(status))
214 hmat_revision = tbl->revision;
215 switch (hmat_revision) {
220 pr_notice("Ignoring HMAT: Unknown revision:%d\n", hmat_revision);
224 for (i = ACPI_HMAT_TYPE_ADDRESS_RANGE; i < ACPI_HMAT_TYPE_RESERVED; i++) {
225 if (acpi_table_parse_entries(ACPI_SIG_HMAT,
226 sizeof(struct acpi_table_hmat), i,
227 hmat_parse_subtable, 0) < 0) {
228 pr_notice("Ignoring HMAT: Invalid table");
236 subsys_initcall(hmat_init);