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