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(fd: STDIN_FILENO, maxfd, set: &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, set: &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, set: &rfds);
303 if (acpi_aml_log_count())
304 maxfd = acpi_aml_set_fd(fd: STDOUT_FILENO, maxfd, set: &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, set: &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(fd: STDIN_FILENO, flags: 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

source code of linux/tools/power/acpi/tools/acpidbg/acpidbg.c