• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2007 Semihalf
4  *
5  * Written by: Rafal Jaworowski <raj@semihalf.com>
6  */
7 
8 #include <config.h>
9 #include <command.h>
10 #include <common.h>
11 #include <env.h>
12 #include <malloc.h>
13 #include <env_internal.h>
14 #include <linux/types.h>
15 #include <api_public.h>
16 #include <u-boot/crc.h>
17 
18 #include "api_private.h"
19 
20 #define DEBUG
21 #undef DEBUG
22 
23 /*****************************************************************************
24  *
25  * This is the API core.
26  *
27  * API_ functions are part of U-Boot code and constitute the lowest level
28  * calls:
29  *
30  *  - they know what values they need as arguments
31  *  - their direct return value pertains to the API_ "shell" itself (0 on
32  *    success, some error code otherwise)
33  *  - if the call returns a value it is buried within arguments
34  *
35  ****************************************************************************/
36 
37 #ifdef DEBUG
38 #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt, ##args); } while (0)
39 #else
40 #define debugf(fmt, args...)
41 #endif
42 
43 typedef	int (*cfp_t)(va_list argp);
44 
45 static int calls_no;
46 
47 /*
48  * pseudo signature:
49  *
50  * int API_getc(int *c)
51  */
API_getc(va_list ap)52 static int API_getc(va_list ap)
53 {
54 	int *c;
55 
56 	if ((c = (int *)va_arg(ap, uintptr_t)) == NULL)
57 		return API_EINVAL;
58 
59 	*c = getc();
60 	return 0;
61 }
62 
63 /*
64  * pseudo signature:
65  *
66  * int API_tstc(int *c)
67  */
API_tstc(va_list ap)68 static int API_tstc(va_list ap)
69 {
70 	int *t;
71 
72 	if ((t = (int *)va_arg(ap, uintptr_t)) == NULL)
73 		return API_EINVAL;
74 
75 	*t = tstc();
76 	return 0;
77 }
78 
79 /*
80  * pseudo signature:
81  *
82  * int API_putc(char *ch)
83  */
API_putc(va_list ap)84 static int API_putc(va_list ap)
85 {
86 	char *c;
87 
88 	if ((c = (char *)va_arg(ap, uintptr_t)) == NULL)
89 		return API_EINVAL;
90 
91 	putc(*c);
92 	return 0;
93 }
94 
95 /*
96  * pseudo signature:
97  *
98  * int API_puts(char **s)
99  */
API_puts(va_list ap)100 static int API_puts(va_list ap)
101 {
102 	char *s;
103 
104 	if ((s = (char *)va_arg(ap, uintptr_t)) == NULL)
105 		return API_EINVAL;
106 
107 	puts(s);
108 	return 0;
109 }
110 
111 /*
112  * pseudo signature:
113  *
114  * int API_reset(void)
115  */
API_reset(va_list ap)116 static int API_reset(va_list ap)
117 {
118 	do_reset(NULL, 0, 0, NULL);
119 
120 	/* NOT REACHED */
121 	return 0;
122 }
123 
124 /*
125  * pseudo signature:
126  *
127  * int API_get_sys_info(struct sys_info *si)
128  *
129  * fill out the sys_info struct containing selected parameters about the
130  * machine
131  */
API_get_sys_info(va_list ap)132 static int API_get_sys_info(va_list ap)
133 {
134 	struct sys_info *si;
135 
136 	si = (struct sys_info *)va_arg(ap, uintptr_t);
137 	if (si == NULL)
138 		return API_ENOMEM;
139 
140 	return (platform_sys_info(si)) ? 0 : API_ENODEV;
141 }
142 
143 /*
144  * pseudo signature:
145  *
146  * int API_udelay(unsigned long *udelay)
147  */
API_udelay(va_list ap)148 static int API_udelay(va_list ap)
149 {
150 	unsigned long *d;
151 
152 	if ((d = (unsigned long *)va_arg(ap, unsigned long)) == NULL)
153 		return API_EINVAL;
154 
155 	udelay(*d);
156 	return 0;
157 }
158 
159 /*
160  * pseudo signature:
161  *
162  * int API_get_timer(unsigned long *current, unsigned long *base)
163  */
API_get_timer(va_list ap)164 static int API_get_timer(va_list ap)
165 {
166 	unsigned long *base, *cur;
167 
168 	cur = (unsigned long *)va_arg(ap, unsigned long);
169 	if (cur == NULL)
170 		return API_EINVAL;
171 
172 	base = (unsigned long *)va_arg(ap, unsigned long);
173 	if (base == NULL)
174 		return API_EINVAL;
175 
176 	*cur = get_timer(*base);
177 	return 0;
178 }
179 
180 
181 /*****************************************************************************
182  *
183  * pseudo signature:
184  *
185  * int API_dev_enum(struct device_info *)
186  *
187  *
188  * cookies uniqely identify the previously enumerated device instance and
189  * provide a hint for what to inspect in current enum iteration:
190  *
191  *   - net: &eth_device struct address from list pointed to by eth_devices
192  *
193  *   - storage: struct blk_desc struct address from &ide_dev_desc[n],
194  *     &scsi_dev_desc[n] and similar tables
195  *
196  ****************************************************************************/
197 
API_dev_enum(va_list ap)198 static int API_dev_enum(va_list ap)
199 {
200 	struct device_info *di;
201 
202 	/* arg is ptr to the device_info struct we are going to fill out */
203 	di = (struct device_info *)va_arg(ap, uintptr_t);
204 	if (di == NULL)
205 		return API_EINVAL;
206 
207 	if (di->cookie == NULL) {
208 		/* start over - clean up enumeration */
209 		dev_enum_reset();	/* XXX shouldn't the name contain 'stor'? */
210 		debugf("RESTART ENUM\n");
211 
212 		/* net device enumeration first */
213 		if (dev_enum_net(di))
214 			return 0;
215 	}
216 
217 	/*
218 	 * The hidden assumption is there can only be one active network
219 	 * device and it is identified upon enumeration (re)start, so there's
220 	 * no point in trying to find network devices in other cases than the
221 	 * (re)start and hence the 'next' device can only be storage
222 	 */
223 	if (!dev_enum_storage(di))
224 		/* make sure we mark there are no more devices */
225 		di->cookie = NULL;
226 
227 	return 0;
228 }
229 
230 
API_dev_open(va_list ap)231 static int API_dev_open(va_list ap)
232 {
233 	struct device_info *di;
234 	int err = 0;
235 
236 	/* arg is ptr to the device_info struct */
237 	di = (struct device_info *)va_arg(ap, uintptr_t);
238 	if (di == NULL)
239 		return API_EINVAL;
240 
241 	/* Allow only one consumer of the device at a time */
242 	if (di->state == DEV_STA_OPEN)
243 		return API_EBUSY;
244 
245 	if (di->cookie == NULL)
246 		return API_ENODEV;
247 
248 	if (di->type & DEV_TYP_STOR)
249 		err = dev_open_stor(di->cookie);
250 
251 	else if (di->type & DEV_TYP_NET)
252 		err = dev_open_net(di->cookie);
253 	else
254 		err = API_ENODEV;
255 
256 	if (!err)
257 		di->state = DEV_STA_OPEN;
258 
259 	return err;
260 }
261 
262 
API_dev_close(va_list ap)263 static int API_dev_close(va_list ap)
264 {
265 	struct device_info *di;
266 	int err = 0;
267 
268 	/* arg is ptr to the device_info struct */
269 	di = (struct device_info *)va_arg(ap, uintptr_t);
270 	if (di == NULL)
271 		return API_EINVAL;
272 
273 	if (di->state == DEV_STA_CLOSED)
274 		return 0;
275 
276 	if (di->cookie == NULL)
277 		return API_ENODEV;
278 
279 	if (di->type & DEV_TYP_STOR)
280 		err = dev_close_stor(di->cookie);
281 
282 	else if (di->type & DEV_TYP_NET)
283 		err = dev_close_net(di->cookie);
284 	else
285 		/*
286 		 * In case of unknown device we cannot change its state, so
287 		 * only return error code
288 		 */
289 		err = API_ENODEV;
290 
291 	if (!err)
292 		di->state = DEV_STA_CLOSED;
293 
294 	return err;
295 }
296 
297 
298 /*
299  * pseudo signature:
300  *
301  * int API_dev_write(
302  *	struct device_info *di,
303  *	void *buf,
304  *	int *len,
305  *	unsigned long *start
306  * )
307  *
308  * buf:	ptr to buffer from where to get the data to send
309  *
310  * len: ptr to length to be read
311  *      - network: len of packet to be sent (in bytes)
312  *      - storage: # of blocks to write (can vary in size depending on define)
313  *
314  * start: ptr to start block (only used for storage devices, ignored for
315  *        network)
316  */
API_dev_write(va_list ap)317 static int API_dev_write(va_list ap)
318 {
319 	struct device_info *di;
320 	void *buf;
321 	lbasize_t *len_stor, act_len_stor;
322 	lbastart_t *start;
323 	int *len_net;
324 	int err = 0;
325 
326 	/* 1. arg is ptr to the device_info struct */
327 	di = (struct device_info *)va_arg(ap, uintptr_t);
328 	if (di == NULL)
329 		return API_EINVAL;
330 
331 	/* XXX should we check if device is open? i.e. the ->state ? */
332 
333 	if (di->cookie == NULL)
334 		return API_ENODEV;
335 
336 	/* 2. arg is ptr to buffer from where to get data to write */
337 	buf = (void *)va_arg(ap, uintptr_t);
338 	if (buf == NULL)
339 		return API_EINVAL;
340 
341 	if (di->type & DEV_TYP_STOR) {
342 		/* 3. arg - ptr to var with # of blocks to write */
343 		len_stor = (lbasize_t *)va_arg(ap, uintptr_t);
344 		if (!len_stor)
345 			return API_EINVAL;
346 		if (*len_stor <= 0)
347 			return API_EINVAL;
348 
349 		/* 4. arg - ptr to var with start block */
350 		start = (lbastart_t *)va_arg(ap, uintptr_t);
351 
352 		act_len_stor = dev_write_stor(di->cookie, buf, *len_stor, *start);
353 		if (act_len_stor != *len_stor) {
354 			debugf("write @ %llu: done %llu out of %llu blocks",
355 				   (uint64_t)blk, (uint64_t)act_len_stor,
356 				   (uint64_t)len_stor);
357 			return API_EIO;
358 		}
359 
360 	} else if (di->type & DEV_TYP_NET) {
361 		/* 3. arg points to the var with length of packet to write */
362 		len_net = (int *)va_arg(ap, uintptr_t);
363 		if (!len_net)
364 			return API_EINVAL;
365 		if (*len_net <= 0)
366 			return API_EINVAL;
367 
368 		err = dev_write_net(di->cookie, buf, *len_net);
369 
370 	} else
371 		err = API_ENODEV;
372 
373 	return err;
374 }
375 
376 
377 /*
378  * pseudo signature:
379  *
380  * int API_dev_read(
381  *	struct device_info *di,
382  *	void *buf,
383  *	size_t *len,
384  *	unsigned long *start
385  *	size_t *act_len
386  * )
387  *
388  * buf:	ptr to buffer where to put the read data
389  *
390  * len: ptr to length to be read
391  *      - network: len of packet to read (in bytes)
392  *      - storage: # of blocks to read (can vary in size depending on define)
393  *
394  * start: ptr to start block (only used for storage devices, ignored for
395  *        network)
396  *
397  * act_len: ptr to where to put the len actually read
398  */
API_dev_read(va_list ap)399 static int API_dev_read(va_list ap)
400 {
401 	struct device_info *di;
402 	void *buf;
403 	lbasize_t *len_stor, *act_len_stor;
404 	lbastart_t *start;
405 	int *len_net, *act_len_net;
406 
407 	/* 1. arg is ptr to the device_info struct */
408 	di = (struct device_info *)va_arg(ap, uintptr_t);
409 	if (di == NULL)
410 		return API_EINVAL;
411 
412 	/* XXX should we check if device is open? i.e. the ->state ? */
413 
414 	if (di->cookie == NULL)
415 		return API_ENODEV;
416 
417 	/* 2. arg is ptr to buffer from where to put the read data */
418 	buf = (void *)va_arg(ap, uintptr_t);
419 	if (buf == NULL)
420 		return API_EINVAL;
421 
422 	if (di->type & DEV_TYP_STOR) {
423 		/* 3. arg - ptr to var with # of blocks to read */
424 		len_stor = (lbasize_t *)va_arg(ap, uintptr_t);
425 		if (!len_stor)
426 			return API_EINVAL;
427 		if (*len_stor <= 0)
428 			return API_EINVAL;
429 
430 		/* 4. arg - ptr to var with start block */
431 		start = (lbastart_t *)va_arg(ap, uintptr_t);
432 
433 		/* 5. arg - ptr to var where to put the len actually read */
434 		act_len_stor = (lbasize_t *)va_arg(ap, uintptr_t);
435 		if (!act_len_stor)
436 			return API_EINVAL;
437 
438 		*act_len_stor = dev_read_stor(di->cookie, buf, *len_stor, *start);
439 
440 	} else if (di->type & DEV_TYP_NET) {
441 
442 		/* 3. arg points to the var with length of packet to read */
443 		len_net = (int *)va_arg(ap, uintptr_t);
444 		if (!len_net)
445 			return API_EINVAL;
446 		if (*len_net <= 0)
447 			return API_EINVAL;
448 
449 		/* 4. - ptr to var where to put the len actually read */
450 		act_len_net = (int *)va_arg(ap, uintptr_t);
451 		if (!act_len_net)
452 			return API_EINVAL;
453 
454 		*act_len_net = dev_read_net(di->cookie, buf, *len_net);
455 
456 	} else
457 		return API_ENODEV;
458 
459 	return 0;
460 }
461 
462 
463 /*
464  * pseudo signature:
465  *
466  * int API_env_get(const char *name, char **value)
467  *
468  * name: ptr to name of env var
469  */
API_env_get(va_list ap)470 static int API_env_get(va_list ap)
471 {
472 	char *name, **value;
473 
474 	if ((name = (char *)va_arg(ap, uintptr_t)) == NULL)
475 		return API_EINVAL;
476 	if ((value = (char **)va_arg(ap, uintptr_t)) == NULL)
477 		return API_EINVAL;
478 
479 	*value = env_get(name);
480 
481 	return 0;
482 }
483 
484 /*
485  * pseudo signature:
486  *
487  * int API_env_set(const char *name, const char *value)
488  *
489  * name: ptr to name of env var
490  *
491  * value: ptr to value to be set
492  */
API_env_set(va_list ap)493 static int API_env_set(va_list ap)
494 {
495 	char *name, *value;
496 
497 	if ((name = (char *)va_arg(ap, uintptr_t)) == NULL)
498 		return API_EINVAL;
499 	if ((value = (char *)va_arg(ap, uintptr_t)) == NULL)
500 		return API_EINVAL;
501 
502 	env_set(name, value);
503 
504 	return 0;
505 }
506 
507 /*
508  * pseudo signature:
509  *
510  * int API_env_enum(const char *last, char **next)
511  *
512  * last: ptr to name of env var found in last iteration
513  */
API_env_enum(va_list ap)514 static int API_env_enum(va_list ap)
515 {
516 	int i, buflen;
517 	char *last, **next, *s;
518 	struct env_entry *match, search;
519 	static char *var;
520 
521 	last = (char *)va_arg(ap, unsigned long);
522 
523 	if ((next = (char **)va_arg(ap, uintptr_t)) == NULL)
524 		return API_EINVAL;
525 
526 	if (last == NULL) {
527 		var = NULL;
528 		i = 0;
529 	} else {
530 		var = strdup(last);
531 		s = strchr(var, '=');
532 		if (s != NULL)
533 			*s = 0;
534 		search.key = var;
535 		i = hsearch_r(search, ENV_FIND, &match, &env_htab, 0);
536 		if (i == 0) {
537 			i = API_EINVAL;
538 			goto done;
539 		}
540 	}
541 
542 	/* match the next entry after i */
543 	i = hmatch_r("", i, &match, &env_htab);
544 	if (i == 0)
545 		goto done;
546 	buflen = strlen(match->key) + strlen(match->data) + 2;
547 	var = realloc(var, buflen);
548 	snprintf(var, buflen, "%s=%s", match->key, match->data);
549 	*next = var;
550 	return 0;
551 
552 done:
553 	free(var);
554 	var = NULL;
555 	*next = NULL;
556 	return i;
557 }
558 
559 /*
560  * pseudo signature:
561  *
562  * int API_display_get_info(int type, struct display_info *di)
563  */
API_display_get_info(va_list ap)564 static int API_display_get_info(va_list ap)
565 {
566 	int type;
567 	struct display_info *di;
568 
569 	type = va_arg(ap, int);
570 	di = va_arg(ap, struct display_info *);
571 
572 	return display_get_info(type, di);
573 }
574 
575 /*
576  * pseudo signature:
577  *
578  * int API_display_draw_bitmap(ulong bitmap, int x, int y)
579  */
API_display_draw_bitmap(va_list ap)580 static int API_display_draw_bitmap(va_list ap)
581 {
582 	ulong bitmap;
583 	int x, y;
584 
585 	bitmap = va_arg(ap, ulong);
586 	x = va_arg(ap, int);
587 	y = va_arg(ap, int);
588 
589 	return display_draw_bitmap(bitmap, x, y);
590 }
591 
592 /*
593  * pseudo signature:
594  *
595  * void API_display_clear(void)
596  */
API_display_clear(va_list ap)597 static int API_display_clear(va_list ap)
598 {
599 	display_clear();
600 	return 0;
601 }
602 
603 static cfp_t calls_table[API_MAXCALL] = { NULL, };
604 
605 /*
606  * The main syscall entry point - this is not reentrant, only one call is
607  * serviced until finished.
608  *
609  * e.g. syscall(1, int *, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
610  *
611  * call:	syscall number
612  *
613  * retval:	points to the return value placeholder, this is the place the
614  *		syscall puts its return value, if NULL the caller does not
615  *		expect a return value
616  *
617  * ...		syscall arguments (variable number)
618  *
619  * returns:	0 if the call not found, 1 if serviced
620  */
syscall(int call,int * retval,...)621 int syscall(int call, int *retval, ...)
622 {
623 	va_list	ap;
624 	int rv;
625 
626 	if (call < 0 || call >= calls_no) {
627 		debugf("invalid call #%d\n", call);
628 		return 0;
629 	}
630 
631 	if (calls_table[call] == NULL) {
632 		debugf("syscall #%d does not have a handler\n", call);
633 		return 0;
634 	}
635 
636 	va_start(ap, retval);
637 	rv = calls_table[call](ap);
638 	if (retval != NULL)
639 		*retval = rv;
640 
641 	return 1;
642 }
643 
api_init(void)644 void api_init(void)
645 {
646 	struct api_signature *sig;
647 
648 	/* TODO put this into linker set one day... */
649 	calls_table[API_RSVD] = NULL;
650 	calls_table[API_GETC] = &API_getc;
651 	calls_table[API_PUTC] = &API_putc;
652 	calls_table[API_TSTC] = &API_tstc;
653 	calls_table[API_PUTS] = &API_puts;
654 	calls_table[API_RESET] = &API_reset;
655 	calls_table[API_GET_SYS_INFO] = &API_get_sys_info;
656 	calls_table[API_UDELAY] = &API_udelay;
657 	calls_table[API_GET_TIMER] = &API_get_timer;
658 	calls_table[API_DEV_ENUM] = &API_dev_enum;
659 	calls_table[API_DEV_OPEN] = &API_dev_open;
660 	calls_table[API_DEV_CLOSE] = &API_dev_close;
661 	calls_table[API_DEV_READ] = &API_dev_read;
662 	calls_table[API_DEV_WRITE] = &API_dev_write;
663 	calls_table[API_ENV_GET] = &API_env_get;
664 	calls_table[API_ENV_SET] = &API_env_set;
665 	calls_table[API_ENV_ENUM] = &API_env_enum;
666 	calls_table[API_DISPLAY_GET_INFO] = &API_display_get_info;
667 	calls_table[API_DISPLAY_DRAW_BITMAP] = &API_display_draw_bitmap;
668 	calls_table[API_DISPLAY_CLEAR] = &API_display_clear;
669 	calls_no = API_MAXCALL;
670 
671 	debugf("API initialized with %d calls\n", calls_no);
672 
673 	dev_stor_init();
674 
675 	/*
676 	 * Produce the signature so the API consumers can find it
677 	 */
678 	sig = malloc(sizeof(struct api_signature));
679 	if (sig == NULL) {
680 		printf("API: could not allocate memory for the signature!\n");
681 		return;
682 	}
683 
684 	env_set_hex("api_address", (unsigned long)sig);
685 	debugf("API sig @ 0x%lX\n", (unsigned long)sig);
686 	memcpy(sig->magic, API_SIG_MAGIC, 8);
687 	sig->version = API_SIG_VERSION;
688 	sig->syscall = &syscall;
689 	sig->checksum = 0;
690 	sig->checksum = crc32(0, (unsigned char *)sig,
691 			      sizeof(struct api_signature));
692 	debugf("syscall entry: 0x%lX\n", (unsigned long)sig->syscall);
693 }
694 
platform_set_mr(struct sys_info * si,unsigned long start,unsigned long size,int flags)695 void platform_set_mr(struct sys_info *si, unsigned long start, unsigned long size,
696 			int flags)
697 {
698 	int i;
699 
700 	if (!si->mr || !size || (flags == 0))
701 		return;
702 
703 	/* find free slot */
704 	for (i = 0; i < si->mr_no; i++)
705 		if (si->mr[i].flags == 0) {
706 			/* insert new mem region */
707 			si->mr[i].start = start;
708 			si->mr[i].size = size;
709 			si->mr[i].flags = flags;
710 			return;
711 		}
712 }
713