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