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