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