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/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
  43typedef int (*cfp_t)(va_list argp);
  44
  45static int calls_no;
  46
  47/*
  48 * pseudo signature:
  49 *
  50 * int API_getc(int *c)
  51 */
  52static 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 */
  68static 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 */
  84static 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 */
 100static 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 */
 116static 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 */
 132static 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 */
 148static 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 */
 164static 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
 198static 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
 231static 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
 263static 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 */
 317static 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 */
 399static 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 */
 470static 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 */
 493static 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 */
 514static 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
 552done:
 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 */
 564static 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 */
 580static 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 */
 597static int API_display_clear(va_list ap)
 598{
 599        display_clear();
 600        return 0;
 601}
 602
 603static 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 */
 621int 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
 644void 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
 695void 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