Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */ 2 : : 3 : : #include <errno.h> 4 : : #include <fcntl.h> 5 : : #include <stddef.h> 6 : : #include <stdint.h> 7 : : #include <string.h> 8 : : #include <unistd.h> 9 : : 10 : : #include "acpi-fpdt.h" 11 : : #include "alloc-util.h" 12 : : #include "fd-util.h" 13 : : #include "fileio.h" 14 : : #include "time-util.h" 15 : : 16 : : struct acpi_table_header { 17 : : char signature[4]; 18 : : uint32_t length; 19 : : uint8_t revision; 20 : : uint8_t checksum; 21 : : char oem_id[6]; 22 : : char oem_table_id[8]; 23 : : uint32_t oem_revision; 24 : : char asl_compiler_id[4]; 25 : : uint32_t asl_compiler_revision; 26 : : }; 27 : : 28 : : enum { 29 : : ACPI_FPDT_TYPE_BOOT = 0, 30 : : ACPI_FPDT_TYPE_S3PERF = 1, 31 : : }; 32 : : 33 : : struct acpi_fpdt_header { 34 : : uint16_t type; 35 : : uint8_t length; 36 : : uint8_t revision; 37 : : uint8_t reserved[4]; 38 : : uint64_t ptr; 39 : : }; 40 : : 41 : : struct acpi_fpdt_boot_header { 42 : : char signature[4]; 43 : : uint32_t length; 44 : : }; 45 : : 46 : : enum { 47 : : ACPI_FPDT_S3PERF_RESUME_REC = 0, 48 : : ACPI_FPDT_S3PERF_SUSPEND_REC = 1, 49 : : ACPI_FPDT_BOOT_REC = 2, 50 : : }; 51 : : 52 : : struct acpi_fpdt_boot { 53 : : uint16_t type; 54 : : uint8_t length; 55 : : uint8_t revision; 56 : : uint8_t reserved[4]; 57 : : uint64_t reset_end; 58 : : uint64_t load_start; 59 : : uint64_t startup_start; 60 : : uint64_t exit_services_entry; 61 : : uint64_t exit_services_exit; 62 : : }; 63 : : 64 : 8 : int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { 65 : 8 : _cleanup_free_ char *buf = NULL; 66 : : struct acpi_table_header *tbl; 67 : 8 : size_t l = 0; 68 : : struct acpi_fpdt_header *rec; 69 : : int r; 70 : 8 : uint64_t ptr = 0; 71 : 8 : _cleanup_close_ int fd = -1; 72 : : struct acpi_fpdt_boot_header hbrec; 73 : : struct acpi_fpdt_boot brec; 74 : : 75 : 8 : r = read_full_file("/sys/firmware/acpi/tables/FPDT", &buf, &l); 76 [ + - ]: 8 : if (r < 0) 77 : 8 : return r; 78 : : 79 [ # # ]: 0 : if (l < sizeof(struct acpi_table_header) + sizeof(struct acpi_fpdt_header)) 80 : 0 : return -EINVAL; 81 : : 82 : 0 : tbl = (struct acpi_table_header *)buf; 83 [ # # ]: 0 : if (l != tbl->length) 84 : 0 : return -EINVAL; 85 : : 86 [ # # ]: 0 : if (memcmp(tbl->signature, "FPDT", 4) != 0) 87 : 0 : return -EINVAL; 88 : : 89 : : /* find Firmware Basic Boot Performance Pointer Record */ 90 : 0 : for (rec = (struct acpi_fpdt_header *)(buf + sizeof(struct acpi_table_header)); 91 [ # # ]: 0 : (char *)rec < buf + l; 92 : 0 : rec = (struct acpi_fpdt_header *)((char *)rec + rec->length)) { 93 [ # # ]: 0 : if (rec->length <= 0) 94 : 0 : break; 95 [ # # ]: 0 : if (rec->type != ACPI_FPDT_TYPE_BOOT) 96 : 0 : continue; 97 [ # # ]: 0 : if (rec->length != sizeof(struct acpi_fpdt_header)) 98 : 0 : continue; 99 : : 100 : 0 : ptr = rec->ptr; 101 : 0 : break; 102 : : } 103 : : 104 [ # # ]: 0 : if (ptr == 0) 105 : 0 : return -ENODATA; 106 : : 107 : : /* read Firmware Basic Boot Performance Data Record */ 108 : 0 : fd = open("/dev/mem", O_CLOEXEC|O_RDONLY); 109 [ # # ]: 0 : if (fd < 0) 110 : 0 : return -errno; 111 : : 112 : 0 : l = pread(fd, &hbrec, sizeof(struct acpi_fpdt_boot_header), ptr); 113 [ # # ]: 0 : if (l != sizeof(struct acpi_fpdt_boot_header)) 114 : 0 : return -EINVAL; 115 : : 116 [ # # ]: 0 : if (memcmp(hbrec.signature, "FBPT", 4) != 0) 117 : 0 : return -EINVAL; 118 : : 119 [ # # ]: 0 : if (hbrec.length < sizeof(struct acpi_fpdt_boot_header) + sizeof(struct acpi_fpdt_boot)) 120 : 0 : return -EINVAL; 121 : : 122 : 0 : l = pread(fd, &brec, sizeof(struct acpi_fpdt_boot), ptr + sizeof(struct acpi_fpdt_boot_header)); 123 [ # # ]: 0 : if (l != sizeof(struct acpi_fpdt_boot)) 124 : 0 : return -EINVAL; 125 : : 126 [ # # ]: 0 : if (brec.length != sizeof(struct acpi_fpdt_boot)) 127 : 0 : return -EINVAL; 128 : : 129 [ # # ]: 0 : if (brec.type != ACPI_FPDT_BOOT_REC) 130 : 0 : return -EINVAL; 131 : : 132 [ # # ]: 0 : if (brec.exit_services_exit == 0) 133 : : /* Non-UEFI compatible boot. */ 134 : 0 : return -ENODATA; 135 : : 136 [ # # # # ]: 0 : if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start) 137 : 0 : return -EINVAL; 138 [ # # ]: 0 : if (brec.exit_services_exit > NSEC_PER_HOUR) 139 : 0 : return -EINVAL; 140 : : 141 [ # # ]: 0 : if (loader_start) 142 : 0 : *loader_start = brec.startup_start / 1000; 143 [ # # ]: 0 : if (loader_exit) 144 : 0 : *loader_exit = brec.exit_services_exit / 1000; 145 : : 146 : 0 : return 0; 147 : : }