1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * ACPI AML interfacing userspace utility 4 * 5 * Copyright (C) 2015, Intel Corporation 6 * Authors: Lv Zheng <lv.zheng@intel.com> 7 */ 8 9#include <acpi/acpi.h> 10 11/* Headers not included by include/acpi/platform/aclinux.h */ 12#include <unistd.h> 13#include <stdio.h> 14#include <stdlib.h> 15#include <string.h> 16#include <error.h> 17#include <stdbool.h> 18#include <fcntl.h> 19#include <assert.h> 20#include <sys/select.h> 21#include "../../../../../include/linux/circ_buf.h" 22 23#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg" 24#define ACPI_AML_SEC_TICK 1 25#define ACPI_AML_USEC_PEEK 200 26#define ACPI_AML_BUF_SIZE 4096 27 28#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */ 29#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */ 30#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */ 31 32#define ACPI_AML_LOG_START 0x00 33#define ACPI_AML_PROMPT_START 0x01 34#define ACPI_AML_PROMPT_STOP 0x02 35#define ACPI_AML_LOG_STOP 0x03 36#define ACPI_AML_PROMPT_ROLL 0x04 37 38#define ACPI_AML_INTERACTIVE 0x00 39#define ACPI_AML_BATCH 0x01 40 41#define circ_count(circ) \ 42 (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 43#define circ_count_to_end(circ) \ 44 (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 45#define circ_space(circ) \ 46 (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 47#define circ_space_to_end(circ) \ 48 (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE)) 49 50#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc) 51#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc) 52#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc) 53#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc) 54 55#define ACPI_AML_DO(_fd, _op, _buf, _ret) \ 56 do { \ 57 _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \ 58 if (_ret == 0) { \ 59 fprintf(stderr, \ 60 "%s %s pipe closed.\n", #_buf, #_op); \ 61 return; \ 62 } \ 63 } while (0) 64#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \ 65 do { \ 66 _ret = acpi_aml_##_op##_batch_##_buf(_fd, \ 67 &acpi_aml_##_buf##_crc); \ 68 if (_ret == 0) \ 69 return; \ 70 } while (0) 71 72 73static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE]; 74static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE]; 75static struct circ_buf acpi_aml_cmd_crc = { 76 .buf = acpi_aml_cmd_buf, 77 .head = 0, 78 .tail = 0, 79}; 80static struct circ_buf acpi_aml_log_crc = { 81 .buf = acpi_aml_log_buf, 82 .head = 0, 83 .tail = 0, 84}; 85static const char *acpi_aml_file_path = ACPI_AML_FILE; 86static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE; 87static bool acpi_aml_exit; 88 89static bool acpi_aml_batch_drain; 90static unsigned long acpi_aml_batch_state; 91static char acpi_aml_batch_prompt; 92static char acpi_aml_batch_roll; 93static unsigned long acpi_aml_log_state; 94static char *acpi_aml_batch_cmd = NULL; 95static char *acpi_aml_batch_pos = NULL; 96 97static int acpi_aml_set_fl(int fd, int flags) 98{ 99 int ret; 100 101 ret = fcntl(fd, F_GETFL, 0); 102 if (ret < 0) { 103 perror("fcntl(F_GETFL)"); 104 return ret; 105 } 106 flags |= ret; 107 ret = fcntl(fd, F_SETFL, flags); 108 if (ret < 0) { 109 perror("fcntl(F_SETFL)"); 110 return ret; 111 } 112 return ret; 113} 114 115static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set) 116{ 117 if (fd > maxfd) 118 maxfd = fd; 119 FD_SET(fd, set); 120 return maxfd; 121} 122 123static int acpi_aml_read(int fd, struct circ_buf *crc) 124{ 125 char *p; 126 int len; 127 128 p = &crc->buf[crc->head]; 129 len = circ_space_to_end(crc); 130 len = read(fd, p, len); 131 if (len < 0) 132 perror("read"); 133 else if (len > 0) 134 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1); 135 return len; 136} 137 138static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc) 139{ 140 char *p; 141 int len; 142 int remained = strlen(acpi_aml_batch_pos); 143 144 p = &crc->buf[crc->head]; 145 len = circ_space_to_end(crc); 146 if (len > remained) { 147 memcpy(p, acpi_aml_batch_pos, remained); 148 acpi_aml_batch_pos += remained; 149 len = remained; 150 } else { 151 memcpy(p, acpi_aml_batch_pos, len); 152 acpi_aml_batch_pos += len; 153 } 154 if (len > 0) 155 crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1); 156 return len; 157} 158 159static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc) 160{ 161 char *p; 162 int len; 163 int ret = 0; 164 165 p = &crc->buf[crc->head]; 166 len = circ_space_to_end(crc); 167 while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) { 168 if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) { 169 *p = acpi_aml_batch_roll; 170 len = 1; 171 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1); 172 ret += 1; 173 acpi_aml_log_state = ACPI_AML_LOG_START; 174 } else { 175 len = read(fd, p, 1); 176 if (len <= 0) { 177 if (len < 0) 178 perror("read"); 179 ret = len; 180 break; 181 } 182 } 183 switch (acpi_aml_log_state) { 184 case ACPI_AML_LOG_START: 185 if (*p == '\n') 186 acpi_aml_log_state = ACPI_AML_PROMPT_START; 187 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1); 188 ret += 1; 189 break; 190 case ACPI_AML_PROMPT_START: 191 if (*p == ACPI_DEBUGGER_COMMAND_PROMPT || 192 *p == ACPI_DEBUGGER_EXECUTE_PROMPT) { 193 acpi_aml_batch_prompt = *p; 194 acpi_aml_log_state = ACPI_AML_PROMPT_STOP; 195 } else { 196 if (*p != '\n') 197 acpi_aml_log_state = ACPI_AML_LOG_START; 198 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1); 199 ret += 1; 200 } 201 break; 202 case ACPI_AML_PROMPT_STOP: 203 if (*p == ' ') { 204 acpi_aml_log_state = ACPI_AML_LOG_STOP; 205 acpi_aml_exit = true; 206 } else { 207 /* Roll back */ 208 acpi_aml_log_state = ACPI_AML_PROMPT_ROLL; 209 acpi_aml_batch_roll = *p; 210 *p = acpi_aml_batch_prompt; 211 crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1); 212 ret += 1; 213 } 214 break; 215 default: 216 assert(0); 217 break; 218 } 219 } 220 return ret; 221} 222 223static int acpi_aml_write(int fd, struct circ_buf *crc) 224{ 225 char *p; 226 int len; 227 228 p = &crc->buf[crc->tail]; 229 len = circ_count_to_end(crc); 230 len = write(fd, p, len); 231 if (len < 0) 232 perror("write"); 233 else if (len > 0) 234 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1); 235 return len; 236} 237 238static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc) 239{ 240 char *p; 241 int len; 242 243 p = &crc->buf[crc->tail]; 244 len = circ_count_to_end(crc); 245 if (!acpi_aml_batch_drain) { 246 len = write(fd, p, len); 247 if (len < 0) 248 perror("write"); 249 } 250 if (len > 0) 251 crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1); 252 return len; 253} 254 255static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc) 256{ 257 int len; 258 259 len = acpi_aml_write(fd, crc); 260 if (circ_count_to_end(crc) == 0) 261 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG; 262 return len; 263} 264 265static void acpi_aml_loop(int fd) 266{ 267 fd_set rfds; 268 fd_set wfds; 269 struct timeval tv; 270 int ret; 271 int maxfd = 0; 272 273 if (acpi_aml_mode == ACPI_AML_BATCH) { 274 acpi_aml_log_state = ACPI_AML_LOG_START; 275 acpi_aml_batch_pos = acpi_aml_batch_cmd; 276 if (acpi_aml_batch_drain) 277 acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG; 278 else 279 acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD; 280 } 281 acpi_aml_exit = false; 282 while (!acpi_aml_exit) { 283 tv.tv_sec = ACPI_AML_SEC_TICK; 284 tv.tv_usec = 0; 285 FD_ZERO(&rfds); 286 FD_ZERO(&wfds); 287 288 if (acpi_aml_cmd_space()) { 289 if (acpi_aml_mode == ACPI_AML_INTERACTIVE) 290 maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds); 291 else if (strlen(acpi_aml_batch_pos) && 292 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD) 293 ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret); 294 } 295 if (acpi_aml_cmd_count() && 296 (acpi_aml_mode == ACPI_AML_INTERACTIVE || 297 acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)) 298 maxfd = acpi_aml_set_fd(fd, maxfd, &wfds); 299 if (acpi_aml_log_space() && 300 (acpi_aml_mode == ACPI_AML_INTERACTIVE || 301 acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG)) 302 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds); 303 if (acpi_aml_log_count()) 304 maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds); 305 306 ret = select(maxfd+1, &rfds, &wfds, NULL, &tv); 307 if (ret < 0) { 308 perror("select"); 309 break; 310 } 311 if (ret > 0) { 312 if (FD_ISSET(STDIN_FILENO, &rfds)) 313 ACPI_AML_DO(STDIN_FILENO, read, cmd, ret); 314 if (FD_ISSET(fd, &wfds)) { 315 if (acpi_aml_mode == ACPI_AML_BATCH) 316 ACPI_AML_BATCH_DO(fd, write, cmd, ret); 317 else 318 ACPI_AML_DO(fd, write, cmd, ret); 319 } 320 if (FD_ISSET(fd, &rfds)) { 321 if (acpi_aml_mode == ACPI_AML_BATCH) 322 ACPI_AML_BATCH_DO(fd, read, log, ret); 323 else 324 ACPI_AML_DO(fd, read, log, ret); 325 } 326 if (FD_ISSET(STDOUT_FILENO, &wfds)) { 327 if (acpi_aml_mode == ACPI_AML_BATCH) 328 ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret); 329 else 330 ACPI_AML_DO(STDOUT_FILENO, write, log, ret); 331 } 332 } 333 } 334} 335 336static bool acpi_aml_readable(int fd) 337{ 338 fd_set rfds; 339 struct timeval tv; 340 int ret; 341 int maxfd = 0; 342 343 tv.tv_sec = 0; 344 tv.tv_usec = ACPI_AML_USEC_PEEK; 345 FD_ZERO(&rfds); 346 maxfd = acpi_aml_set_fd(fd, maxfd, &rfds); 347 ret = select(maxfd+1, &rfds, NULL, NULL, &tv); 348 if (ret < 0) 349 perror("select"); 350 if (ret > 0 && FD_ISSET(fd, &rfds)) 351 return true; 352 return false; 353} 354 355/* 356 * This is a userspace IO flush implementation, replying on the prompt 357 * characters and can be turned into a flush() call after kernel implements 358 * .flush() filesystem operation. 359 */ 360static void acpi_aml_flush(int fd) 361{ 362 while (acpi_aml_readable(fd)) { 363 acpi_aml_batch_drain = true; 364 acpi_aml_loop(fd); 365 acpi_aml_batch_drain = false; 366 } 367} 368 369void usage(FILE *file, char *progname) 370{ 371 fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname); 372 fprintf(file, "\nOptions:\n"); 373 fprintf(file, " -b Specify command to be executed in batch mode\n"); 374 fprintf(file, " -f Specify interface file other than"); 375 fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n"); 376 fprintf(file, " -h Print this help message\n"); 377} 378 379int main(int argc, char **argv) 380{ 381 int fd = -1; 382 int ch; 383 int len; 384 int ret = EXIT_SUCCESS; 385 386 while ((ch = getopt(argc, argv, "b:f:h")) != -1) { 387 switch (ch) { 388 case 'b': 389 if (acpi_aml_batch_cmd) { 390 fprintf(stderr, "Already specify %s\n", 391 acpi_aml_batch_cmd); 392 ret = EXIT_FAILURE; 393 goto exit; 394 } 395 len = strlen(optarg); 396 acpi_aml_batch_cmd = calloc(len + 2, 1); 397 if (!acpi_aml_batch_cmd) { 398 perror("calloc"); 399 ret = EXIT_FAILURE; 400 goto exit; 401 } 402 memcpy(acpi_aml_batch_cmd, optarg, len); 403 acpi_aml_batch_cmd[len] = '\n'; 404 acpi_aml_mode = ACPI_AML_BATCH; 405 break; 406 case 'f': 407 acpi_aml_file_path = optarg; 408 break; 409 case 'h': 410 usage(stdout, argv[0]); 411 goto exit; 412 break; 413 case '?': 414 default: 415 usage(stderr, argv[0]); 416 ret = EXIT_FAILURE; 417 goto exit; 418 break; 419 } 420 } 421 422 fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK); 423 if (fd < 0) { 424 perror("open"); 425 ret = EXIT_FAILURE; 426 goto exit; 427 } 428 acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK); 429 acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK); 430 431 if (acpi_aml_mode == ACPI_AML_BATCH) 432 acpi_aml_flush(fd); 433 acpi_aml_loop(fd); 434 435exit: 436 if (fd >= 0) 437 close(fd); 438 if (acpi_aml_batch_cmd) 439 free(acpi_aml_batch_cmd); 440 return ret; 441} 442