acpi-fpdt.c revision f576cd2092bc40f9998415cdc3caf10035d4743a
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/***
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2013 Kay Sievers
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen***/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <stdlib.h>
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <stdio.h>
be077570f779664ed87b50f60608df9fbe258821Tom Gundersen#include <stdint.h>
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <string.h>
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen#include <unistd.h>
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen#include <fcntl.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <sys/types.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen#include <util.h>
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen#include <fileio.h>
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen#include <time-util.h>
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen#include <acpi-fpdt.h>
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenstruct acpi_table_header {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen char signature[4];
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen uint32_t length;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen uint8_t revision;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen uint8_t checksum;
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen char oem_id[6];
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen char oem_table_id[8];
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint32_t oem_revision;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen char asl_compiler_id[4];
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint32_t asl_compiler_revision;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen};
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenenum {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen ACPI_FPDT_TYPE_BOOT = 0,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen ACPI_FPDT_TYPE_S3PERF = 1,
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen};
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenstruct acpi_fpdt_header {
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen uint16_t type;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen uint8_t length;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen uint8_t revision;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen uint8_t reserved[4];
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen uint64_t ptr;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen};
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersenstruct acpi_fpdt_boot_header {
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen char signature[4];
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen uint32_t length;
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen};
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenenum {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen ACPI_FPDT_S3PERF_RESUME_REC = 0,
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen ACPI_FPDT_S3PERF_SUSPEND_REC = 1,
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen ACPI_FPDT_BOOT_REC = 2,
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen};
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstruct acpi_fpdt_boot {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint16_t type;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint8_t length;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint8_t revision;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint8_t reserved[4];
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t reset_end;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t load_start;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t startup_start;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t exit_services_entry;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t exit_services_exit;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen};
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenint acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen _cleanup_free_ char *buf = NULL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen struct acpi_table_header *tbl;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen size_t l;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen struct acpi_fpdt_header *rec;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen int r;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen uint64_t ptr = 0;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen _cleanup_close_ int fd = -1;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen struct acpi_fpdt_boot_header hbrec;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen struct acpi_fpdt_boot brec;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (r < 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return r;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header))
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen tbl = (struct acpi_table_header *)buf;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (l != tbl->length)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (memcmp(tbl->signature, "FPDT", 4) != 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* find Firmware Basic Boot Performance Pointer Record */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header));
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen (char *)rec < buf + l;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (rec->length <= 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen break;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (rec->type != ACPI_FPDT_TYPE_BOOT)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen continue;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (rec->length != sizeof(struct acpi_fpdt_header))
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen continue;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen ptr = rec->ptr;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen break;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen }
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (ptr == 0)
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen /* read Firmware Basic Boot Performance Data Record */
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen fd = open("/dev/mem", O_CLOEXEC|O_RDONLY);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen if (fd < 0)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -errno;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (l != sizeof(struct acpi_fpdt_boot_header))
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen if (memcmp(hbrec.signature, "FBPT", 4) != 0)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot))
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header));
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (l != sizeof(struct acpi_fpdt_boot))
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (brec.length != sizeof(struct acpi_fpdt_boot))
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen return -EINVAL;
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen
20af7091de0cdf92bf299addfc3f96c3ef805bd8Tom Gundersen if (brec.type != ACPI_FPDT_BOOT_REC)
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen return -EINVAL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (brec.exit_services_exit > NSEC_PER_HOUR)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return -EINVAL;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (loader_start)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen *loader_start = brec.startup_start / 1000;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen if (loader_exit)
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen *loader_exit = brec.exit_services_exit / 1000;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return 0;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen}
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen