uboot/drivers/misc/cros_ec_sandbox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Chromium OS cros_ec driver - sandbox emulation
   4 *
   5 * Copyright (c) 2013 The Chromium OS Authors.
   6 */
   7
   8#include <common.h>
   9#include <cros_ec.h>
  10#include <dm.h>
  11#include <ec_commands.h>
  12#include <errno.h>
  13#include <hash.h>
  14#include <malloc.h>
  15#include <os.h>
  16#include <u-boot/sha256.h>
  17#include <spi.h>
  18#include <asm/state.h>
  19#include <asm/sdl.h>
  20#include <linux/input.h>
  21
  22/*
  23 * Ultimately it shold be possible to connect an Chrome OS EC emulation
  24 * to U-Boot and remove all of this code. But this provides a test
  25 * environment for bringing up chromeos_sandbox and demonstrating its
  26 * utility.
  27 *
  28 * This emulation includes the following:
  29 *
  30 * 1. Emulation of the keyboard, by converting keypresses received from SDL
  31 * into key scan data, passed back from the EC as key scan messages. The
  32 * key layout is read from the device tree.
  33 *
  34 * 2. Emulation of vboot context - so this can be read/written as required.
  35 *
  36 * 3. Save/restore of EC state, so that the vboot context, flash memory
  37 * contents and current image can be preserved across boots. This is important
  38 * since the EC is supposed to continue running even if the AP resets.
  39 *
  40 * 4. Some event support, in particular allowing Escape to be pressed on boot
  41 * to enter recovery mode. The EC passes this to U-Boot through the normal
  42 * event message.
  43 *
  44 * 5. Flash read/write/erase support, so that software sync works. The
  45 * protect messages are supported but no protection is implemented.
  46 *
  47 * 6. Hashing of the EC image, again to support software sync.
  48 *
  49 * Other features can be added, although a better path is probably to link
  50 * the EC image in with U-Boot (Vic has demonstrated a prototype for this).
  51 */
  52
  53#define KEYBOARD_ROWS   8
  54#define KEYBOARD_COLS   13
  55
  56/* A single entry of the key matrix */
  57struct ec_keymatrix_entry {
  58        int row;        /* key matrix row */
  59        int col;        /* key matrix column */
  60        int keycode;    /* corresponding linux key code */
  61};
  62
  63/**
  64 * struct ec_state - Information about the EC state
  65 *
  66 * @vbnv_context: Vboot context data stored by EC
  67 * @ec_config: FDT config information about the EC (e.g. flashmap)
  68 * @flash_data: Contents of flash memory
  69 * @flash_data_len: Size of flash memory
  70 * @current_image: Current image the EC is running
  71 * @matrix_count: Number of keys to decode in matrix
  72 * @matrix: Information about keyboard matrix
  73 * @keyscan: Current keyscan information (bit set for each row/column pressed)
  74 * @recovery_req: Keyboard recovery requested
  75 */
  76struct ec_state {
  77        u8 vbnv_context[EC_VBNV_BLOCK_SIZE_V2];
  78        struct fdt_cros_ec ec_config;
  79        uint8_t *flash_data;
  80        int flash_data_len;
  81        enum ec_current_image current_image;
  82        int matrix_count;
  83        struct ec_keymatrix_entry *matrix;      /* the key matrix info */
  84        uint8_t keyscan[KEYBOARD_COLS];
  85        bool recovery_req;
  86} s_state, *g_state;
  87
  88/**
  89 * cros_ec_read_state() - read the sandbox EC state from the state file
  90 *
  91 * If data is available, then blob and node will provide access to it. If
  92 * not this function sets up an empty EC.
  93 *
  94 * @param blob: Pointer to device tree blob, or NULL if no data to read
  95 * @param node: Node offset to read from
  96 */
  97static int cros_ec_read_state(const void *blob, int node)
  98{
  99        struct ec_state *ec = &s_state;
 100        const char *prop;
 101        int len;
 102
 103        /* Set everything to defaults */
 104        ec->current_image = EC_IMAGE_RO;
 105        if (!blob)
 106                return 0;
 107
 108        /* Read the data if available */
 109        ec->current_image = fdtdec_get_int(blob, node, "current-image",
 110                                           EC_IMAGE_RO);
 111        prop = fdt_getprop(blob, node, "vbnv-context", &len);
 112        if (prop && len == sizeof(ec->vbnv_context))
 113                memcpy(ec->vbnv_context, prop, len);
 114
 115        prop = fdt_getprop(blob, node, "flash-data", &len);
 116        if (prop) {
 117                ec->flash_data_len = len;
 118                ec->flash_data = os_malloc(len);
 119                if (!ec->flash_data)
 120                        return -ENOMEM;
 121                memcpy(ec->flash_data, prop, len);
 122                debug("%s: Loaded EC flash data size %#x\n", __func__, len);
 123        }
 124
 125        return 0;
 126}
 127
 128/**
 129 * cros_ec_write_state() - Write out our state to the state file
 130 *
 131 * The caller will ensure that there is a node ready for the state. The node
 132 * may already contain the old state, in which case it is overridden.
 133 *
 134 * @param blob: Device tree blob holding state
 135 * @param node: Node to write our state into
 136 */
 137static int cros_ec_write_state(void *blob, int node)
 138{
 139        struct ec_state *ec = g_state;
 140
 141        /* We are guaranteed enough space to write basic properties */
 142        fdt_setprop_u32(blob, node, "current-image", ec->current_image);
 143        fdt_setprop(blob, node, "vbnv-context", ec->vbnv_context,
 144                    sizeof(ec->vbnv_context));
 145        return state_setprop(node, "flash-data", ec->flash_data,
 146                             ec->ec_config.flash.length);
 147}
 148
 149SANDBOX_STATE_IO(cros_ec, "google,cros-ec", cros_ec_read_state,
 150                 cros_ec_write_state);
 151
 152/**
 153 * Return the number of bytes used in the specified image.
 154 *
 155 * This is the actual size of code+data in the image, as opposed to the
 156 * amount of space reserved in flash for that image. This code is similar to
 157 * that used by the real EC code base.
 158 *
 159 * @param ec    Current emulated EC state
 160 * @param entry Flash map entry containing the image to check
 161 * @return actual image size in bytes, 0 if the image contains no content or
 162 * error.
 163 */
 164static int get_image_used(struct ec_state *ec, struct fmap_entry *entry)
 165{
 166        int size;
 167
 168        /*
 169         * Scan backwards looking for 0xea byte, which is by definition the
 170         * last byte of the image.  See ec.lds.S for how this is inserted at
 171         * the end of the image.
 172         */
 173        for (size = entry->length - 1;
 174             size > 0 && ec->flash_data[entry->offset + size] != 0xea;
 175             size--)
 176                ;
 177
 178        return size ? size + 1 : 0;  /* 0xea byte IS part of the image */
 179}
 180
 181/**
 182 * Read the key matrix from the device tree
 183 *
 184 * Keymap entries in the fdt take the form of 0xRRCCKKKK where
 185 * RR=Row CC=Column KKKK=Key Code
 186 *
 187 * @param ec    Current emulated EC state
 188 * @param node  Keyboard node of device tree containing keyscan information
 189 * @return 0 if ok, -1 on error
 190 */
 191static int keyscan_read_fdt_matrix(struct ec_state *ec, ofnode node)
 192{
 193        const u32 *cell;
 194        int upto;
 195        int len;
 196
 197        cell = ofnode_get_property(node, "linux,keymap", &len);
 198        ec->matrix_count = len / 4;
 199        ec->matrix = calloc(ec->matrix_count, sizeof(*ec->matrix));
 200        if (!ec->matrix) {
 201                debug("%s: Out of memory for key matrix\n", __func__);
 202                return -1;
 203        }
 204
 205        /* Now read the data */
 206        for (upto = 0; upto < ec->matrix_count; upto++) {
 207                struct ec_keymatrix_entry *matrix = &ec->matrix[upto];
 208                u32 word;
 209
 210                word = fdt32_to_cpu(*cell++);
 211                matrix->row = word >> 24;
 212                matrix->col = (word >> 16) & 0xff;
 213                matrix->keycode = word & 0xffff;
 214
 215                /* Hard-code some sanity limits for now */
 216                if (matrix->row >= KEYBOARD_ROWS ||
 217                    matrix->col >= KEYBOARD_COLS) {
 218                        debug("%s: Matrix pos out of range (%d,%d)\n",
 219                              __func__, matrix->row, matrix->col);
 220                        return -1;
 221                }
 222        }
 223
 224        if (upto != ec->matrix_count) {
 225                debug("%s: Read mismatch from key matrix\n", __func__);
 226                return -1;
 227        }
 228
 229        return 0;
 230}
 231
 232/**
 233 * Return the next keyscan message contents
 234 *
 235 * @param ec    Current emulated EC state
 236 * @param scan  Place to put keyscan bytes for the keyscan message (must hold
 237 *              enough space for a full keyscan)
 238 * @return number of bytes of valid scan data
 239 */
 240static int cros_ec_keyscan(struct ec_state *ec, uint8_t *scan)
 241{
 242        const struct ec_keymatrix_entry *matrix;
 243        int bytes = KEYBOARD_COLS;
 244        int key[8];     /* allow up to 8 keys to be pressed at once */
 245        int count;
 246        int i;
 247
 248        memset(ec->keyscan, '\0', bytes);
 249        count = sandbox_sdl_scan_keys(key, ARRAY_SIZE(key));
 250
 251        /* Look up keycode in matrix */
 252        for (i = 0, matrix = ec->matrix; i < ec->matrix_count; i++, matrix++) {
 253                bool found;
 254                int j;
 255
 256                for (found = false, j = 0; j < count; j++) {
 257                        if (matrix->keycode == key[j])
 258                                found = true;
 259                }
 260
 261                if (found) {
 262                        debug("%d: %d,%d\n", matrix->keycode, matrix->row,
 263                              matrix->col);
 264                        ec->keyscan[matrix->col] |= 1 << matrix->row;
 265                }
 266        }
 267
 268        memcpy(scan, ec->keyscan, bytes);
 269        return bytes;
 270}
 271
 272/**
 273 * Process an emulated EC command
 274 *
 275 * @param ec            Current emulated EC state
 276 * @param req_hdr       Pointer to request header
 277 * @param req_data      Pointer to body of request
 278 * @param resp_hdr      Pointer to place to put response header
 279 * @param resp_data     Pointer to place to put response data, if any
 280 * @return length of response data, or 0 for no response data, or -1 on error
 281 */
 282static int process_cmd(struct ec_state *ec,
 283                       struct ec_host_request *req_hdr, const void *req_data,
 284                       struct ec_host_response *resp_hdr, void *resp_data)
 285{
 286        int len;
 287
 288        /* TODO(sjg@chromium.org): Check checksums */
 289        debug("EC command %#0x\n", req_hdr->command);
 290
 291        switch (req_hdr->command) {
 292        case EC_CMD_HELLO: {
 293                const struct ec_params_hello *req = req_data;
 294                struct ec_response_hello *resp = resp_data;
 295
 296                resp->out_data = req->in_data + 0x01020304;
 297                len = sizeof(*resp);
 298                break;
 299        }
 300        case EC_CMD_GET_VERSION: {
 301                struct ec_response_get_version *resp = resp_data;
 302
 303                strcpy(resp->version_string_ro, "sandbox_ro");
 304                strcpy(resp->version_string_rw, "sandbox_rw");
 305                resp->current_image = ec->current_image;
 306                debug("Current image %d\n", resp->current_image);
 307                len = sizeof(*resp);
 308                break;
 309        }
 310        case EC_CMD_VBNV_CONTEXT: {
 311                const struct ec_params_vbnvcontext *req = req_data;
 312                struct ec_response_vbnvcontext *resp = resp_data;
 313
 314                switch (req->op) {
 315                case EC_VBNV_CONTEXT_OP_READ:
 316                        /* TODO(sjg@chromium.org): Support full-size context */
 317                        memcpy(resp->block, ec->vbnv_context,
 318                               EC_VBNV_BLOCK_SIZE);
 319                        len = 16;
 320                        break;
 321                case EC_VBNV_CONTEXT_OP_WRITE:
 322                        /* TODO(sjg@chromium.org): Support full-size context */
 323                        memcpy(ec->vbnv_context, req->block,
 324                               EC_VBNV_BLOCK_SIZE);
 325                        len = 0;
 326                        break;
 327                default:
 328                        printf("   ** Unknown vbnv_context command %#02x\n",
 329                               req->op);
 330                        return -1;
 331                }
 332                break;
 333        }
 334        case EC_CMD_REBOOT_EC: {
 335                const struct ec_params_reboot_ec *req = req_data;
 336
 337                printf("Request reboot type %d\n", req->cmd);
 338                switch (req->cmd) {
 339                case EC_REBOOT_DISABLE_JUMP:
 340                        len = 0;
 341                        break;
 342                case EC_REBOOT_JUMP_RW:
 343                        ec->current_image = EC_IMAGE_RW;
 344                        len = 0;
 345                        break;
 346                default:
 347                        puts("   ** Unknown type");
 348                        return -1;
 349                }
 350                break;
 351        }
 352        case EC_CMD_HOST_EVENT_GET_B: {
 353                struct ec_response_host_event_mask *resp = resp_data;
 354
 355                resp->mask = 0;
 356                if (ec->recovery_req) {
 357                        resp->mask |= EC_HOST_EVENT_MASK(
 358                                        EC_HOST_EVENT_KEYBOARD_RECOVERY);
 359                }
 360
 361                len = sizeof(*resp);
 362                break;
 363        }
 364        case EC_CMD_VBOOT_HASH: {
 365                const struct ec_params_vboot_hash *req = req_data;
 366                struct ec_response_vboot_hash *resp = resp_data;
 367                struct fmap_entry *entry;
 368                int ret, size;
 369
 370                entry = &ec->ec_config.region[EC_FLASH_REGION_ACTIVE];
 371
 372                switch (req->cmd) {
 373                case EC_VBOOT_HASH_RECALC:
 374                case EC_VBOOT_HASH_GET:
 375                        size = SHA256_SUM_LEN;
 376                        len = get_image_used(ec, entry);
 377                        ret = hash_block("sha256",
 378                                         ec->flash_data + entry->offset,
 379                                         len, resp->hash_digest, &size);
 380                        if (ret) {
 381                                printf("   ** hash_block() failed\n");
 382                                return -1;
 383                        }
 384                        resp->status = EC_VBOOT_HASH_STATUS_DONE;
 385                        resp->hash_type = EC_VBOOT_HASH_TYPE_SHA256;
 386                        resp->digest_size = size;
 387                        resp->reserved0 = 0;
 388                        resp->offset = entry->offset;
 389                        resp->size = len;
 390                        len = sizeof(*resp);
 391                        break;
 392                default:
 393                        printf("   ** EC_CMD_VBOOT_HASH: Unknown command %d\n",
 394                               req->cmd);
 395                        return -1;
 396                }
 397                break;
 398        }
 399        case EC_CMD_FLASH_PROTECT: {
 400                const struct ec_params_flash_protect *req = req_data;
 401                struct ec_response_flash_protect *resp = resp_data;
 402                uint32_t expect = EC_FLASH_PROTECT_ALL_NOW |
 403                                EC_FLASH_PROTECT_ALL_AT_BOOT;
 404
 405                printf("mask=%#x, flags=%#x\n", req->mask, req->flags);
 406                if (req->flags == expect || req->flags == 0) {
 407                        resp->flags = req->flags ? EC_FLASH_PROTECT_ALL_NOW :
 408                                                                0;
 409                        resp->valid_flags = EC_FLASH_PROTECT_ALL_NOW;
 410                        resp->writable_flags = 0;
 411                        len = sizeof(*resp);
 412                } else {
 413                        puts("   ** unexpected flash protect request\n");
 414                        return -1;
 415                }
 416                break;
 417        }
 418        case EC_CMD_FLASH_REGION_INFO: {
 419                const struct ec_params_flash_region_info *req = req_data;
 420                struct ec_response_flash_region_info *resp = resp_data;
 421                struct fmap_entry *entry;
 422
 423                switch (req->region) {
 424                case EC_FLASH_REGION_RO:
 425                case EC_FLASH_REGION_ACTIVE:
 426                case EC_FLASH_REGION_WP_RO:
 427                        entry = &ec->ec_config.region[req->region];
 428                        resp->offset = entry->offset;
 429                        resp->size = entry->length;
 430                        len = sizeof(*resp);
 431                        printf("EC flash region %d: offset=%#x, size=%#x\n",
 432                               req->region, resp->offset, resp->size);
 433                        break;
 434                default:
 435                        printf("** Unknown flash region %d\n", req->region);
 436                        return -1;
 437                }
 438                break;
 439        }
 440        case EC_CMD_FLASH_ERASE: {
 441                const struct ec_params_flash_erase *req = req_data;
 442
 443                memset(ec->flash_data + req->offset,
 444                       ec->ec_config.flash_erase_value,
 445                       req->size);
 446                len = 0;
 447                break;
 448        }
 449        case EC_CMD_FLASH_WRITE: {
 450                const struct ec_params_flash_write *req = req_data;
 451
 452                memcpy(ec->flash_data + req->offset, req + 1, req->size);
 453                len = 0;
 454                break;
 455        }
 456        case EC_CMD_MKBP_STATE:
 457                len = cros_ec_keyscan(ec, resp_data);
 458                break;
 459        case EC_CMD_ENTERING_MODE:
 460                len = 0;
 461                break;
 462        default:
 463                printf("   ** Unknown EC command %#02x\n", req_hdr->command);
 464                return -1;
 465        }
 466
 467        return len;
 468}
 469
 470int cros_ec_sandbox_packet(struct udevice *udev, int out_bytes, int in_bytes)
 471{
 472        struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
 473        struct ec_state *ec = dev_get_priv(dev->dev);
 474        struct ec_host_request *req_hdr = (struct ec_host_request *)dev->dout;
 475        const void *req_data = req_hdr + 1;
 476        struct ec_host_response *resp_hdr = (struct ec_host_response *)dev->din;
 477        void *resp_data = resp_hdr + 1;
 478        int len;
 479
 480        len = process_cmd(ec, req_hdr, req_data, resp_hdr, resp_data);
 481        if (len < 0)
 482                return len;
 483
 484        resp_hdr->struct_version = 3;
 485        resp_hdr->result = EC_RES_SUCCESS;
 486        resp_hdr->data_len = len;
 487        resp_hdr->reserved = 0;
 488        len += sizeof(*resp_hdr);
 489        resp_hdr->checksum = 0;
 490        resp_hdr->checksum = (uint8_t)
 491                -cros_ec_calc_checksum((const uint8_t *)resp_hdr, len);
 492
 493        return in_bytes;
 494}
 495
 496void cros_ec_check_keyboard(struct udevice *dev)
 497{
 498        struct ec_state *ec = dev_get_priv(dev);
 499        ulong start;
 500
 501        printf("Press keys for EC to detect on reset (ESC=recovery)...");
 502        start = get_timer(0);
 503        while (get_timer(start) < 1000)
 504                ;
 505        putc('\n');
 506        if (!sandbox_sdl_key_pressed(KEY_ESC)) {
 507                ec->recovery_req = true;
 508                printf("   - EC requests recovery\n");
 509        }
 510}
 511
 512int cros_ec_probe(struct udevice *dev)
 513{
 514        struct ec_state *ec = dev->priv;
 515        struct cros_ec_dev *cdev = dev->uclass_priv;
 516        struct udevice *keyb_dev;
 517        ofnode node;
 518        int err;
 519
 520        memcpy(ec, &s_state, sizeof(*ec));
 521        err = cros_ec_decode_ec_flash(dev, &ec->ec_config);
 522        if (err) {
 523                debug("%s: Cannot device EC flash\n", __func__);
 524                return err;
 525        }
 526
 527        node = ofnode_null();
 528        for (device_find_first_child(dev, &keyb_dev);
 529             keyb_dev;
 530             device_find_next_child(&keyb_dev)) {
 531                if (device_get_uclass_id(keyb_dev) == UCLASS_KEYBOARD) {
 532                        node = dev_ofnode(keyb_dev);
 533                        break;
 534                }
 535        }
 536        if (!ofnode_valid(node)) {
 537                debug("%s: No cros_ec keyboard found\n", __func__);
 538        } else if (keyscan_read_fdt_matrix(ec, node)) {
 539                debug("%s: Could not read key matrix\n", __func__);
 540                return -1;
 541        }
 542
 543        /* If we loaded EC data, check that the length matches */
 544        if (ec->flash_data &&
 545            ec->flash_data_len != ec->ec_config.flash.length) {
 546                printf("EC data length is %x, expected %x, discarding data\n",
 547                       ec->flash_data_len, ec->ec_config.flash.length);
 548                os_free(ec->flash_data);
 549                ec->flash_data = NULL;
 550        }
 551
 552        /* Otherwise allocate the memory */
 553        if (!ec->flash_data) {
 554                ec->flash_data_len = ec->ec_config.flash.length;
 555                ec->flash_data = os_malloc(ec->flash_data_len);
 556                if (!ec->flash_data)
 557                        return -ENOMEM;
 558        }
 559
 560        cdev->dev = dev;
 561        g_state = ec;
 562        return cros_ec_register(dev);
 563}
 564
 565struct dm_cros_ec_ops cros_ec_ops = {
 566        .packet = cros_ec_sandbox_packet,
 567};
 568
 569static const struct udevice_id cros_ec_ids[] = {
 570        { .compatible = "google,cros-ec-sandbox" },
 571        { }
 572};
 573
 574U_BOOT_DRIVER(cros_ec_sandbox) = {
 575        .name           = "cros_ec_sandbox",
 576        .id             = UCLASS_CROS_EC,
 577        .of_match       = cros_ec_ids,
 578        .probe          = cros_ec_probe,
 579        .priv_auto_alloc_size = sizeof(struct ec_state),
 580        .ops            = &cros_ec_ops,
 581};
 582