Linux 6.7-rc7
[linux-modified.git] / tools / power / acpi / tools / acpidbg / acpidbg.c
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
73 static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
74 static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
75 static struct circ_buf acpi_aml_cmd_crc = {
76         .buf = acpi_aml_cmd_buf,
77         .head = 0,
78         .tail = 0,
79 };
80 static struct circ_buf acpi_aml_log_crc = {
81         .buf = acpi_aml_log_buf,
82         .head = 0,
83         .tail = 0,
84 };
85 static const char *acpi_aml_file_path = ACPI_AML_FILE;
86 static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
87 static bool acpi_aml_exit;
88
89 static bool acpi_aml_batch_drain;
90 static unsigned long acpi_aml_batch_state;
91 static char acpi_aml_batch_prompt;
92 static char acpi_aml_batch_roll;
93 static unsigned long acpi_aml_log_state;
94 static char *acpi_aml_batch_cmd = NULL;
95 static char *acpi_aml_batch_pos = NULL;
96
97 static 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
115 static 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
123 static 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
138 static 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
159 static 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
223 static 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
238 static 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
255 static 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
265 static 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
336 static 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  */
360 static 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
369 void 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
379 int 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
435 exit:
436         if (fd >= 0)
437                 close(fd);
438         if (acpi_aml_batch_cmd)
439                 free(acpi_aml_batch_cmd);
440         return ret;
441 }