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