• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
acpi_aml_set_fl(int fd,int flags)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 
acpi_aml_set_fd(int fd,int maxfd,fd_set * set)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 
acpi_aml_read(int fd,struct circ_buf * crc)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 
acpi_aml_read_batch_cmd(int unused,struct circ_buf * crc)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 
acpi_aml_read_batch_log(int fd,struct circ_buf * crc)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 
acpi_aml_write(int fd,struct circ_buf * crc)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 
acpi_aml_write_batch_log(int fd,struct circ_buf * crc)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 
acpi_aml_write_batch_cmd(int fd,struct circ_buf * crc)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 
acpi_aml_loop(int fd)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 
acpi_aml_readable(int fd)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  */
acpi_aml_flush(int fd)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 
usage(FILE * file,char * progname)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 
main(int argc,char ** argv)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 }
442