1 /*
2 * Copyright © 2015-2018 Intel
3 * Copyright © 2015-2020 Inria. All rights reserved.
4 * See COPYING in top-level directory.
5 */
6
7 #include "private/autogen/config.h"
8
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21
22 #define KERNEL_SMBIOS_SYSFS "/sys/firmware/dmi/entries"
23
24 /* official strings, found at least in Intel S7200AP boards (also used by Cray) and some SuperMicro boards */
25 #define KNL_INTEL_GROUP_STRING "Group: Knights Landing Information"
26 #define KNM_INTEL_GROUP_STRING "Group: Knights Mill Information"
27 /* unexpected strings, found at least in Dell C6320p BIOS <=1.4.1 */
28 #define KNL_DELL_GROUP_STRING "Knights Landing Association"
29
30 static const char *allowed_group_strings[] =
31 {
32 KNL_INTEL_GROUP_STRING,
33 KNM_INTEL_GROUP_STRING,
34 KNL_DELL_GROUP_STRING
35 };
36
37 /* Header is common part of all SMBIOS entries */
38 struct smbios_header
39 {
40 uint8_t type;
41 uint8_t length;
42 uint16_t handle;
43 };
44
45 struct smbios_group
46 {
47 uint8_t group_name;
48 };
49
50 /* This structrures are padded by compiler
51 * So we hardcode size of the struct and use it
52 * instead of sizeof() */
53 #define GROUP_ENTRY_SIZE 3
54 struct smbios_group_entry
55 {
56 uint8_t type;
57 uint16_t handle;
58 };
59
60 /* KNL header is similar as SMBIOS header
61 * decided to add it for readability */
62 #define SMBIOS_KNL_HEADER_SIZE 7
63 struct knl_smbios_header
64 {
65 uint8_t type;
66 uint8_t length;
67 uint16_t handle;
68 uint16_t member_id;
69 uint8_t member_name;
70 };
71
72 /* general info data */
73 #define SMBIOS_KNL_GENERAL_INFO_SIZE 5
74 struct knl_general_info
75 {
76 uint8_t supp_cluster_mode;
77 uint8_t cluster_mode;
78 uint8_t supp_memory_mode;
79 uint8_t memory_mode;
80 uint8_t cache_info;
81 };
82
83 /* memory info */
84 #define SMBIOS_KNL_EDC_INFO_SIZE 9
85 struct knl_edc_info
86 {
87 uint8_t mcdram_present;
88 uint8_t mcdram_enabled;
89 uint8_t allowed_channels;
90 uint8_t reserved[4];
91 uint8_t mcdram_info_size;
92 uint8_t mcdram_info_count;
93 };
94
95 /* mcdram controller structure */
96 struct knl_mcdram_info {
97 uint32_t status;
98 uint8_t controller;
99 uint8_t channel;
100 uint16_t size64MB;
101 uint8_t product_revision;
102 uint8_t fwmajor_revision;
103 uint8_t fwminor_revision;
104 uint8_t fwpatch_revision;
105 };
106
107 /* internal data */
108 struct parser_data
109 {
110 uint64_t mcdram_regular;
111 uint64_t mcdram_cache;
112 int cluster_mode;
113 int memory_mode;
114 int cache_info;
115 int type_count;
116 int knl_types[64];
117 };
118
119 enum cluster_mode
120 {
121 QUADRANT = 1,
122 HEMISPHERE = 2,
123 SNC4 = 4,
124 SNC2 = 8,
125 ALL2ALL = 16
126 };
127
128 enum memory_mode
129 {
130 CACHE = 1,
131 FLAT = 2,
132 HYBRID = 4
133 };
134
135 enum hybrid_cache
136 {
137 H25 = 1,
138 H50 = 2,
139 H100 = 4 /* Incorrect but possible value */
140 };
141
get_file_buffer(const char * file,char * buffer,int size)142 static int get_file_buffer(const char *file, char *buffer, int size)
143 {
144 FILE *f;
145
146 printf(" File = %s\n", file);
147
148 if (!buffer) {
149 fprintf(stderr, "Unable to allocate buffer\n");
150 return 0;
151 }
152
153 f = fopen(file, "rb");
154 if (!f) {
155 fprintf(stderr, "Unable to open %s (%s)\n", file, strerror(errno));
156 return 0;
157 }
158
159 size = fread(buffer, 1, size, f);
160 if (size == 0) {
161 fprintf(stderr, "Unable to read file\n");
162 fclose(f);
163 return 0;
164 }
165 printf(" Read %d bytes\n", size);
166
167 fclose(f);
168 return size;
169 }
170
check_entry(struct smbios_header * h,const char * end,const char * query)171 static int check_entry(struct smbios_header *h, const char *end, const char *query)
172 {
173 char *group_strings = (char*)h + h->length;
174 do {
175 int len = strlen(group_strings);
176 /* SMBIOS string entries end with "\0\0"
177 * if length is 0 break and return
178 * */
179 if (len == 0)
180 break;
181
182 printf(" Looking for \"%s\" in group string \"%s\"\n", query, group_strings);
183 if (!strncmp(group_strings, query, len))
184 return 1;
185
186 group_strings += len;
187 } while(group_strings < end);
188
189 return 0;
190 }
191
is_phi_group(struct smbios_header * h,const char * end)192 static int is_phi_group(struct smbios_header *h, const char *end)
193 {
194 unsigned i;
195 if (h->type != 14) {
196 fprintf(stderr, "SMBIOS table is not group table\n");
197 return 0;
198 }
199
200 for (i = 0; i < sizeof(allowed_group_strings)/sizeof(char*); i++) {
201 if (check_entry(h, end, allowed_group_strings[i]))
202 return 1;
203 }
204
205 return 0;
206 }
207
208 #define KNL_MEMBER_ID_GENERAL 0x1
209 #define KNL_MEMBER_ID_EDC 0x2
210
211 #define PATH_SIZE 512
212 #define SMBIOS_FILE_BUF_SIZE 4096
213 #define KNL_MCDRAM_SIZE (16ULL*1024*1024*1024)
214
process_smbios_group(const char * input_fsroot,char * dir_name,struct parser_data * data)215 static int process_smbios_group(const char *input_fsroot, char *dir_name, struct parser_data *data)
216 {
217 char path[PATH_SIZE];
218 char file_buf[SMBIOS_FILE_BUF_SIZE];
219 struct smbios_header *h;
220 char *p;
221 char *end;
222 int size;
223 int i;
224
225 snprintf(path, PATH_SIZE-1, "%s/" KERNEL_SMBIOS_SYSFS "/%s/raw", input_fsroot, dir_name);
226 path[PATH_SIZE-1] = 0;
227
228 size = get_file_buffer(path, file_buf, SMBIOS_FILE_BUF_SIZE);
229 if (!size) {
230 fprintf(stderr, "Unable to read raw table file\n");
231 return -1;
232 }
233
234 h = (struct smbios_header*)file_buf;
235 end = file_buf+size;
236 if (!is_phi_group(h, end)) {
237 printf(" Failed to find Phi group\n");
238 fprintf(stderr, "SMBIOS table does not contain Xeon Phi entries\n");
239 return -1;
240 }
241 printf(" Found Phi group\n");
242
243 p = file_buf + sizeof(struct smbios_header) + sizeof(struct smbios_group);
244 if ((char*)p >= end) {
245 fprintf(stderr, "SMBIOS table does not have entries\n");
246 return -1;
247 }
248
249 end = file_buf+h->length;
250
251 i = 0;
252 for (; p < end; i++, p+=3) {
253 struct smbios_group_entry *e = (struct smbios_group_entry*)p;
254 data->knl_types[i] = e->type;
255 printf(" Found Xeon Phi type = %d\n", e->type);
256 }
257
258 data->type_count = i;
259 return 0;
260 }
261
process_knl_entry(const char * input_fsroot,char * dir_name,struct parser_data * data)262 static int process_knl_entry(const char *input_fsroot, char *dir_name, struct parser_data *data)
263 {
264 char path[PATH_SIZE];
265 char file_buf[SMBIOS_FILE_BUF_SIZE];
266 char *end;
267 int size;
268 struct knl_smbios_header *h;
269
270 snprintf(path, PATH_SIZE-1, "%s/" KERNEL_SMBIOS_SYSFS "/%s/raw", input_fsroot, dir_name);
271 path[PATH_SIZE-1] = 0;
272
273 size = get_file_buffer(path, file_buf, SMBIOS_FILE_BUF_SIZE);
274 if (!size) {
275 fprintf(stderr, "Unable to read raw table file\n");
276 return -1;
277 }
278
279 end = file_buf+size;
280 h = (struct knl_smbios_header*)file_buf;
281 if (h->member_id & KNL_MEMBER_ID_GENERAL) {
282 struct knl_general_info *info =
283 (struct knl_general_info*) (file_buf+SMBIOS_KNL_HEADER_SIZE);
284 printf(" Getting general Xeon Phi info\n");
285 data->cluster_mode = info->cluster_mode;
286 data->memory_mode = info->memory_mode;
287 data->cache_info = info->cache_info;
288 } else if (h->member_id & KNL_MEMBER_ID_EDC) {
289 struct knl_edc_info *info = (struct knl_edc_info*)(file_buf+SMBIOS_KNL_HEADER_SIZE);
290 if (info->mcdram_present && info->mcdram_enabled) {
291 struct knl_mcdram_info *mi = (struct knl_mcdram_info*)(info + 1);
292 /* we use always smbios size not struct size
293 * as it can change in future.*/
294 int struct_size = info->mcdram_info_size;
295 int i = 0;
296
297 if (0 == struct_size) {
298 printf(" MCDRAM info size is set to 0, falling back to known size\n");
299 struct_size = sizeof(*mi);
300 }
301 printf(" Getting Xeon Phi MCDRAM info. Count=%d struct size=%d\n",
302 (int)info->mcdram_info_count, struct_size);
303 for ( ; i < info->mcdram_info_count; i++) {
304 if ((char*)mi >= end) {
305 fprintf(stderr, "SMBIOS Xeon Phi entry is too small\n");
306 return -1;
307 }
308 printf(" MCDRAM controller %d\n", mi->controller);
309 if (mi->status & 0x1) {
310 printf(" Controller fused\n");
311 } else {
312 data->mcdram_regular += mi->size64MB;
313 printf(" Size = %d MB\n", (int)mi->size64MB*64);
314 }
315 mi = (struct knl_mcdram_info*)(((char*)mi)+struct_size);
316 }
317 /* convert to bytes */
318 printf(" Total MCDRAM %llu MB\n", (long long unsigned int)data->mcdram_regular*64);
319 data->mcdram_regular *= 64*1024*1024;
320 /*
321 * BIOS can expose some MCRAM controllers as fused
322 * When this happens we hardcode MCDRAM size to 16 GB
323 */
324 if (data->mcdram_regular != KNL_MCDRAM_SIZE) {
325 fprintf(stderr, "Not all MCDRAM is exposed in DMI. Please contact BIOS vendor\n");
326 data->mcdram_regular = KNL_MCDRAM_SIZE;
327 }
328
329 } else {
330 data->mcdram_regular = 0;
331 data->mcdram_cache = 0;
332 }
333
334 } else {
335 /* We skip unknown table */
336 fprintf(stderr, "Ignoring unknown SMBIOS entry type=%x\n", h->member_id);
337 }
338
339 return 0;
340 }
get_memory_mode_str(int memory_mode,int hybrid_cache_size)341 static const char* get_memory_mode_str(int memory_mode, int hybrid_cache_size)
342 {
343 switch (memory_mode) {
344 case CACHE: return "Cache";
345 case FLAT: return "Flat";
346 case HYBRID:
347 if (hybrid_cache_size == H25) {
348 return "Hybrid25";
349 } else if (hybrid_cache_size == H50) {
350 return "Hybrid50";
351 }
352 return "Unknown";
353 default:
354 return "Unknown";
355 }
356 }
357
get_cluster_mode_str(int cluster_mode)358 static const char* get_cluster_mode_str(int cluster_mode)
359 {
360 switch (cluster_mode) {
361 case QUADRANT: return "Quadrant";
362 case HEMISPHERE: return "Hemisphere";
363 case ALL2ALL: return "All2All";
364 case SNC2: return "SNC2";
365 case SNC4: return "SNC4";
366 default:
367 return "Unknown";
368 }
369 }
370
print_result(struct parser_data * data,const char * out_file)371 static int print_result(struct parser_data *data, const char *out_file)
372 {
373 int node_count = 0;
374 int fd;
375 FILE *f;
376
377 switch (data->cluster_mode) {
378 case QUADRANT:
379 node_count = 1;
380 break;
381 case HEMISPHERE:
382 node_count = 1;
383 break;
384 case ALL2ALL:
385 node_count = 1;
386 break;
387 case SNC2:
388 node_count = 2;
389 break;
390 case SNC4:
391 node_count = 4;
392 break;
393 default:
394 fprintf(stderr, "Incorrect cluster mode %d\n", data->cluster_mode);
395 return -1;
396 }
397
398 switch (data->memory_mode) {
399 case CACHE:
400 data->mcdram_cache = data->mcdram_regular;
401 data->mcdram_regular = 0;
402 break;
403 case FLAT:
404 data->mcdram_cache = 0;
405 break;
406 case HYBRID:
407 if (data->cache_info == H25) {
408 data->mcdram_cache = data->mcdram_regular/4;
409 } else if (data->cache_info == H50) {
410 data->mcdram_cache = data->mcdram_regular/2;
411 } else if (data->cache_info == H100) {
412 data->mcdram_cache = data->mcdram_regular;
413 } else {
414 fprintf(stderr, "SMBIOS reserved cache info value %d\n", data->cache_info);
415 return -1;
416 }
417 data->mcdram_regular -= data->mcdram_cache;
418 break;
419 default:
420 fprintf(stderr, "Incorrect memory mode %d\n", data->memory_mode);
421 return -1;
422 }
423
424 printf(" Cluster Mode: %s Memory Mode: %s\n",
425 get_cluster_mode_str(data->cluster_mode),
426 get_memory_mode_str(data->memory_mode, data->cache_info));
427 printf(" MCDRAM total = %llu bytes, cache = %llu bytes\n",
428 (long long unsigned int)data->mcdram_regular,
429 (long long unsigned int)data->mcdram_cache);
430 data->mcdram_regular /= node_count;
431 data->mcdram_cache /= node_count;
432 printf(" MCDRAM total = %llu bytes, cache = %llu bytes per node\n",
433 (long long unsigned int)data->mcdram_regular,
434 (long long unsigned int)data->mcdram_cache);
435
436 /* Now we can start printing stuff */
437 /* use open+fdopen so that we can specify the file creation mode */
438 fd = open(out_file, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
439 if (fd < 0) {
440 fprintf(stderr, "Unable to open file `%s' (%s).\n", out_file, strerror(errno));
441 return -1;
442 }
443 f = fdopen(fd, "w");
444 if (!f) {
445 fprintf(stderr, "Unable to fdopen file `%s' (%s).\n", out_file, strerror(errno));
446 close(fd);
447 return -1;
448 }
449
450 fprintf(f, "version: 2\n");
451 /* We cache is equal for node */
452 fprintf(f, "cache_size: %llu\n",
453 (long long unsigned int)data->mcdram_cache);
454 fprintf(f, "associativity: 1\n");// direct-mapped cache
455 fprintf(f, "inclusiveness: 1\n");// inclusive cache
456 fprintf(f, "line_size: 64\n");
457 fprintf(f, "cluster_mode: %s\n", get_cluster_mode_str(data->cluster_mode));
458 fprintf(f, "memory_mode: %s\n", get_memory_mode_str(data->memory_mode, data->cache_info));
459 fflush(f);
460 fclose(f);
461 close(fd);
462 return 0;
463 }
464
465 /**
466 * Seeks SMBIOS sysfs for entry with type
467 */
468 int hwloc_dump_hwdata_knl_smbios(const char *input_fsroot, const char *outfile);
469
hwloc_dump_hwdata_knl_smbios(const char * input_fsroot,const char * outfile)470 int hwloc_dump_hwdata_knl_smbios(const char *input_fsroot, const char *outfile)
471 {
472 DIR *d;
473 int i;
474 struct dirent *dir;
475 struct parser_data data;
476 char path[PATH_SIZE];
477 int err;
478
479 memset(&data, 0, sizeof(data));
480
481 printf("Dumping Xeon Phi SMBIOS Memory-Side Cache information:\n");
482
483 snprintf(path, PATH_SIZE-1, "%s/" KERNEL_SMBIOS_SYSFS, input_fsroot);
484 path[PATH_SIZE-1] = 0;
485
486 d = opendir(path);
487 if (!d) {
488 fprintf(stderr, "Unable to open dmi-sysfs dir: %s", path);
489 return -1;
490 }
491
492 /* process Xeon Phi entries
493 * start with group (type 14, dash os to omit 140 types) then find SMBIOS types for
494 * Knights Landing mcdram indofrmation
495 */
496 while ((dir = readdir(d))) {
497 if (strncmp("14-", dir->d_name, 3) == 0) {
498 err = process_smbios_group(input_fsroot, dir->d_name, &data);
499 if (err < 0) {
500 closedir(d);
501 return err;
502 }
503 }
504 }
505
506 if (!data.type_count) {
507 fprintf (stderr, " Couldn't find any Xeon Phi information.\n");
508 closedir(d);
509 return -1;
510 }
511
512 /* We probably have Xeon Phi type identifiers here */
513 for (i = 0; i < data.type_count; i++) {
514 char tab[16] = {0};
515 int l = snprintf(tab, sizeof(tab)-1, "%d-", data.knl_types[i]);
516 printf("\n");
517 printf (" Seeking dir ̀`%s' %d\n", tab, l);
518 rewinddir(d);
519 while ((dir = readdir(d))) {
520 if (strncmp(dir->d_name, tab, l) == 0) {
521 err = process_knl_entry(input_fsroot, dir->d_name, &data);
522 if (err < 0) {
523 closedir(d);
524 return err;
525 }
526 }
527 }
528 }
529
530 closedir(d);
531
532 return print_result(&data, outfile);
533 }
534