cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

acpi_fpdt.c (6617B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3/*
      4 * FPDT support for exporting boot and suspend/resume performance data
      5 *
      6 * Copyright (C) 2021 Intel Corporation. All rights reserved.
      7 */
      8
      9#define pr_fmt(fmt) "ACPI FPDT: " fmt
     10
     11#include <linux/acpi.h>
     12
     13/*
     14 * FPDT contains ACPI table header and a number of fpdt_subtable_entries.
     15 * Each fpdt_subtable_entry points to a subtable: FBPT or S3PT.
     16 * Each FPDT subtable (FBPT/S3PT) is composed of a fpdt_subtable_header
     17 * and a number of fpdt performance records.
     18 * Each FPDT performance record is composed of a fpdt_record_header and
     19 * performance data fields, for boot or suspend or resume phase.
     20 */
     21enum fpdt_subtable_type {
     22	SUBTABLE_FBPT,
     23	SUBTABLE_S3PT,
     24};
     25
     26struct fpdt_subtable_entry {
     27	u16 type;		/* refer to enum fpdt_subtable_type */
     28	u8 length;
     29	u8 revision;
     30	u32 reserved;
     31	u64 address;		/* physical address of the S3PT/FBPT table */
     32};
     33
     34struct fpdt_subtable_header {
     35	u32 signature;
     36	u32 length;
     37};
     38
     39enum fpdt_record_type {
     40	RECORD_S3_RESUME,
     41	RECORD_S3_SUSPEND,
     42	RECORD_BOOT,
     43};
     44
     45struct fpdt_record_header {
     46	u16 type;		/* refer to enum fpdt_record_type */
     47	u8 length;
     48	u8 revision;
     49};
     50
     51struct resume_performance_record {
     52	struct fpdt_record_header header;
     53	u32 resume_count;
     54	u64 resume_prev;
     55	u64 resume_avg;
     56} __attribute__((packed));
     57
     58struct boot_performance_record {
     59	struct fpdt_record_header header;
     60	u32 reserved;
     61	u64 firmware_start;
     62	u64 bootloader_load;
     63	u64 bootloader_launch;
     64	u64 exitbootservice_start;
     65	u64 exitbootservice_end;
     66} __attribute__((packed));
     67
     68struct suspend_performance_record {
     69	struct fpdt_record_header header;
     70	u64 suspend_start;
     71	u64 suspend_end;
     72} __attribute__((packed));
     73
     74
     75static struct resume_performance_record *record_resume;
     76static struct suspend_performance_record *record_suspend;
     77static struct boot_performance_record *record_boot;
     78
     79#define FPDT_ATTR(phase, name)	\
     80static ssize_t name##_show(struct kobject *kobj,	\
     81		 struct kobj_attribute *attr, char *buf)	\
     82{	\
     83	return sprintf(buf, "%llu\n", record_##phase->name);	\
     84}	\
     85static struct kobj_attribute name##_attr =	\
     86__ATTR(name##_ns, 0444, name##_show, NULL)
     87
     88FPDT_ATTR(resume, resume_prev);
     89FPDT_ATTR(resume, resume_avg);
     90FPDT_ATTR(suspend, suspend_start);
     91FPDT_ATTR(suspend, suspend_end);
     92FPDT_ATTR(boot, firmware_start);
     93FPDT_ATTR(boot, bootloader_load);
     94FPDT_ATTR(boot, bootloader_launch);
     95FPDT_ATTR(boot, exitbootservice_start);
     96FPDT_ATTR(boot, exitbootservice_end);
     97
     98static ssize_t resume_count_show(struct kobject *kobj,
     99				 struct kobj_attribute *attr, char *buf)
    100{
    101	return sprintf(buf, "%u\n", record_resume->resume_count);
    102}
    103
    104static struct kobj_attribute resume_count_attr =
    105__ATTR_RO(resume_count);
    106
    107static struct attribute *resume_attrs[] = {
    108	&resume_count_attr.attr,
    109	&resume_prev_attr.attr,
    110	&resume_avg_attr.attr,
    111	NULL
    112};
    113
    114static const struct attribute_group resume_attr_group = {
    115	.attrs = resume_attrs,
    116	.name = "resume",
    117};
    118
    119static struct attribute *suspend_attrs[] = {
    120	&suspend_start_attr.attr,
    121	&suspend_end_attr.attr,
    122	NULL
    123};
    124
    125static const struct attribute_group suspend_attr_group = {
    126	.attrs = suspend_attrs,
    127	.name = "suspend",
    128};
    129
    130static struct attribute *boot_attrs[] = {
    131	&firmware_start_attr.attr,
    132	&bootloader_load_attr.attr,
    133	&bootloader_launch_attr.attr,
    134	&exitbootservice_start_attr.attr,
    135	&exitbootservice_end_attr.attr,
    136	NULL
    137};
    138
    139static const struct attribute_group boot_attr_group = {
    140	.attrs = boot_attrs,
    141	.name = "boot",
    142};
    143
    144static struct kobject *fpdt_kobj;
    145
    146static int fpdt_process_subtable(u64 address, u32 subtable_type)
    147{
    148	struct fpdt_subtable_header *subtable_header;
    149	struct fpdt_record_header *record_header;
    150	char *signature = (subtable_type == SUBTABLE_FBPT ? "FBPT" : "S3PT");
    151	u32 length, offset;
    152	int result;
    153
    154	subtable_header = acpi_os_map_memory(address, sizeof(*subtable_header));
    155	if (!subtable_header)
    156		return -ENOMEM;
    157
    158	if (strncmp((char *)&subtable_header->signature, signature, 4)) {
    159		pr_info(FW_BUG "subtable signature and type mismatch!\n");
    160		return -EINVAL;
    161	}
    162
    163	length = subtable_header->length;
    164	acpi_os_unmap_memory(subtable_header, sizeof(*subtable_header));
    165
    166	subtable_header = acpi_os_map_memory(address, length);
    167	if (!subtable_header)
    168		return -ENOMEM;
    169
    170	offset = sizeof(*subtable_header);
    171	while (offset < length) {
    172		record_header = (void *)subtable_header + offset;
    173		offset += record_header->length;
    174
    175		switch (record_header->type) {
    176		case RECORD_S3_RESUME:
    177			if (subtable_type != SUBTABLE_S3PT) {
    178				pr_err(FW_BUG "Invalid record %d for subtable %s\n",
    179				     record_header->type, signature);
    180				return -EINVAL;
    181			}
    182			if (record_resume) {
    183				pr_err("Duplicate resume performance record found.\n");
    184				continue;
    185			}
    186			record_resume = (struct resume_performance_record *)record_header;
    187			result = sysfs_create_group(fpdt_kobj, &resume_attr_group);
    188			if (result)
    189				return result;
    190			break;
    191		case RECORD_S3_SUSPEND:
    192			if (subtable_type != SUBTABLE_S3PT) {
    193				pr_err(FW_BUG "Invalid %d for subtable %s\n",
    194				     record_header->type, signature);
    195				continue;
    196			}
    197			if (record_suspend) {
    198				pr_err("Duplicate suspend performance record found.\n");
    199				continue;
    200			}
    201			record_suspend = (struct suspend_performance_record *)record_header;
    202			result = sysfs_create_group(fpdt_kobj, &suspend_attr_group);
    203			if (result)
    204				return result;
    205			break;
    206		case RECORD_BOOT:
    207			if (subtable_type != SUBTABLE_FBPT) {
    208				pr_err(FW_BUG "Invalid %d for subtable %s\n",
    209				     record_header->type, signature);
    210				return -EINVAL;
    211			}
    212			if (record_boot) {
    213				pr_err("Duplicate boot performance record found.\n");
    214				continue;
    215			}
    216			record_boot = (struct boot_performance_record *)record_header;
    217			result = sysfs_create_group(fpdt_kobj, &boot_attr_group);
    218			if (result)
    219				return result;
    220			break;
    221
    222		default:
    223			/* Other types are reserved in ACPI 6.4 spec. */
    224			break;
    225		}
    226	}
    227	return 0;
    228}
    229
    230static int __init acpi_init_fpdt(void)
    231{
    232	acpi_status status;
    233	struct acpi_table_header *header;
    234	struct fpdt_subtable_entry *subtable;
    235	u32 offset = sizeof(*header);
    236
    237	status = acpi_get_table(ACPI_SIG_FPDT, 0, &header);
    238
    239	if (ACPI_FAILURE(status))
    240		return 0;
    241
    242	fpdt_kobj = kobject_create_and_add("fpdt", acpi_kobj);
    243	if (!fpdt_kobj) {
    244		acpi_put_table(header);
    245		return -ENOMEM;
    246	}
    247
    248	while (offset < header->length) {
    249		subtable = (void *)header + offset;
    250		switch (subtable->type) {
    251		case SUBTABLE_FBPT:
    252		case SUBTABLE_S3PT:
    253			fpdt_process_subtable(subtable->address,
    254					      subtable->type);
    255			break;
    256		default:
    257			/* Other types are reserved in ACPI 6.4 spec. */
    258			break;
    259		}
    260		offset += sizeof(*subtable);
    261	}
    262	return 0;
    263}
    264
    265fs_initcall(acpi_init_fpdt);