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