]> xenbits.xensource.com Git - people/aperard/linux-chromebook.git/commitdiff
CHROMIUM: chromeos_arm: factor out vbc access
authorChe-Liang Chiou <clchiou@chromium.org>
Mon, 19 Nov 2012 21:04:22 +0000 (13:04 -0800)
committerGerrit <chrome-bot@google.com>
Thu, 29 Nov 2012 01:59:31 +0000 (17:59 -0800)
Factor out vbc access from chromeos_arm in preparation for abstracting
vbc interface and for supporting access of vbc on ec.

Also, make it try to search device from phandle (though not it does not
fully work yet).

BUG=chrome-os-partner:15609
TEST=manual, add debugging sysfs entry to call
     chromeos_platform_read/write_vboot_context, and test that the
     driver can actually access vboot context on disk.

Change-Id: Iea610e35b2f299926ebd14b4ffbe73566d8ad7c7
Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/38324
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/arm/boot/dts/cros5250-common.dtsi
drivers/platform/Kconfig
drivers/platform/Makefile
drivers/platform/arm/Kconfig
drivers/platform/arm/chromeos_arm.c
drivers/platform/chromeos_vbc_blk.c [new file with mode: 0644]

index 740f5c10e616895baf4d95aa6eefdc0254235ac5..295980ac6787aa45a0ec7645ff7a60d924a823db 100644 (file)
@@ -43,6 +43,7 @@
        firmware {
                chromeos {
                        write-protect-gpio = <&gpd1 6 0 0x10000 0>;
+                       chromeos-vbc-blk = <&mshc_0 0 0 16>;
                };
        };
 
index 7e4a85dc1f1f3e98a60240ac8ef218fcc8763567..d902d26b01c88350fe01c9e493c6d98d03cba4bd 100644 (file)
@@ -17,6 +17,15 @@ config CHROMEOS
          ChromeOS devices. It depends on a lowlevel driver to implement the
          firmware interface on the platform.
 
+config CHROMEOS_VBC_BLK
+       bool
+       depends on CHROMEOS
+
+       ---help---
+         Provides access to vboot context stored on block device.  Select this
+         if you have a ChromeOS firmware that does not support storing vboot
+         context in EC's nvram.
+
 config CHROMEOS_RAMOOPS_RAM_START
         hex "Physical address of preserved RAM"
        depends on RAMOOPS && CHROMEOS
index 3d8174e67d6782a7dc9a0c6d61c34a28eaf8ebf2..96433238999dbe9edbb8c2d5be79006881994540 100644 (file)
@@ -5,3 +5,4 @@ obj-$(CONFIG_ARM)               += arm/
 obj-$(CONFIG_X86)              += x86/
 obj-$(CONFIG_CHROMEOS)         += chromeos.o
 obj-$(CONFIG_MFD_CHROMEOS_EC)  += chromeos_ec-fw.o
+obj-$(CONFIG_CHROMEOS_VBC_BLK) += chromeos_vbc_blk.o
index 816b682a42b445f00d70de78ea15758b97e15554..4f7f2332a0a55b57341aca1c67695b00b130675a 100644 (file)
@@ -16,6 +16,7 @@ if ARM_PLATFORM_DEVICES
 config ARM_CHROMEOS_FIRMWARE
        bool "ChromeOS firmware interface driver"
        select CHROMEOS
+       select CHROMEOS_VBC_BLK
        ---help---
          This driver provides an interface to ChromeOS firmware.
 
index 214a5977f9b3712133e076ef4a1aa516d86d5459..271e67ea1b36285e1808e34256f7ab775ae4511f 100644 (file)
@@ -1,7 +1,4 @@
 /*
- *  ChromeOS platform support code. Glue layer between higher level functions
- *  and per-platform firmware interfaces.
- *
  *  Copyright (C) 2011 The Chromium OS Authors
  *
  *  This program is free software; you can redistribute it and/or modify
 
 #define pr_fmt(fmt) "chromeos_arm: " fmt
 
-#include <linux/chromeos_platform.h>
-#include <linux/ide.h>
 #include <linux/gpio.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 
-
-#include "../chromeos.h"
-
-/* TODO:
- * Do a proper search for the right mmc device to use
- */
-
-#define BLKNV_MAJOR MMC_BLOCK_MAJOR
-#define BLKNV_MINOR 0
-
-/*
- * location where the vboot context data blends into the sector on the
- * MMC device
- */
-static u16 nv_offset, nv_size;
-static u64 nv_lba;
-
-/*
- * Functions to support vboot context on block device. The actual device used
- * is minor 0 of MMC device class, the sector to use is as encoded in
- * firmware_shared_data->nvcxt_lba, the vboot context buffer in the sector
- * starts at offset nv_offset and takes nv_size bytes.
- */
-static void vbc_blk_endio(struct bio *bio, int err)
-{
-       struct completion *c = bio->bi_private;
-       bio->bi_private = (void *)err;
-       complete(c);
-}
-
-static void vbc_blk_submit_bio(struct bio *bio, int rq)
-{
-       DECLARE_COMPLETION_ONSTACK(wait);
-
-       bio->bi_end_io  = vbc_blk_endio;
-       bio->bi_private = &wait;
-       submit_bio(rq, bio);
-       wait_for_completion(&wait);
-}
-
-static int vbc_blk_access(struct page *page, sector_t sector, bool is_read)
-{
-       struct block_device *bdev;
-       struct bio *bio = NULL;
-       dev_t mdev;
-       fmode_t devmode = is_read ? FMODE_READ : FMODE_WRITE;
-       int rq, ret;
-
-       mdev = MKDEV(BLKNV_MAJOR, BLKNV_MINOR);
-       bdev = blkdev_get_by_dev(mdev, devmode, NULL);
-       if (IS_ERR(bdev)) {
-               pr_err("could not open dev=[%d:%d]\n",
-                      BLKNV_MAJOR, BLKNV_MINOR);
-               ret = -EFAULT;
-               goto out;
-       }
-
-       /* map the sector to page */
-       bio = bio_alloc(GFP_NOIO, 1);
-       if (!bio) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       bio->bi_bdev    = bdev;
-       bio->bi_sector  = sector;
-       bio->bi_vcnt    = 1;
-       bio->bi_idx     = 0;
-       bio->bi_size    = SECTOR_SIZE;
-       bio->bi_io_vec[0].bv_page       = page;
-       bio->bi_io_vec[0].bv_len        = SECTOR_SIZE;
-       bio->bi_io_vec[0].bv_offset     = 0;
-
-       /* submit bio */
-       rq = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
-       if (!is_read)
-               rq |= REQ_WRITE;
-
-       vbc_blk_submit_bio(bio, rq);
-
-       /* nvblk_endio passes up any error in bi_private */
-       ret = (int)bio->bi_private;
-out:
-       if (bio)
-               bio_put(bio);
-       if (!is_read) {
-               fsync_bdev(bdev);
-               invalidate_bdev(bdev);
-       }
-       if (bdev)
-               blkdev_put(bdev, devmode);
-       return ret;
-}
-
-/*
- * This function accepts a buffer with exactly nv_size bytes. It reads the
- * appropriate mmc one sector block, extacts the nonvolatile data from there
- * and copies it to the provided buffer.
- */
-static int vbc_blk_read(u8 *data)
-{
-       struct page *page;
-       char *virtual_addr;
-       int ret;
-
-       page = alloc_page(GFP_NOIO);
-       if (!page) {
-               pr_err("page allocation failed\n");
-               return -ENOMEM;
-       }
-
-       virtual_addr = page_address(page);
-       if (!virtual_addr) {
-               pr_err("page not mapped!\n");
-               __free_page(page);
-               return -EFAULT;
-       }
-
-       ret = vbc_blk_access(page, nv_lba, 1);
-       if (ret)
-               goto out;
-
-       memcpy(data, virtual_addr + nv_offset, nv_size);
-       ret = nv_size;
-
-out:
-       __free_page(page);
-       return ret;
-}
-
-/*
- * This function accepts a buffer with exactly nv_size bytes. It reads the
- * appropriate mmc one sector block, blends in the new vboot context contents
- * and then writes the sector back.
- */
-static int vbc_blk_write(const u8 *data)
-{
-       struct page *page;
-       char *virtual_addr;
-       int ret;
-
-       page = alloc_page(GFP_NOIO);
-       if (!page) {
-               pr_err("page allocation failed\n");
-               return -ENOMEM;
-       }
-
-       virtual_addr = page_address(page);
-       if (!virtual_addr) {
-               pr_err("page not mapped!\n");
-               __free_page(page);
-               return -EFAULT;
-       }
-
-       ret = vbc_blk_access(page, nv_lba, 1);
-       if (ret)
-               goto out;
-
-       /*
-        * Sector has been read, lets blend in vboot context data and write
-        * the sector back.
-        */
-       memcpy(virtual_addr + nv_offset, data, nv_size);
-
-       ret = vbc_blk_access(page, nv_lba, 0);
-       if (!ret)
-               ret = nv_size;
-
-out:
-       __free_page(page);
-       return ret;
-}
-
-/*
- * Read the vboot context buffer contents into the user provided space.
- *
- * returns number of bytes copied, or negative error.
- */
-ssize_t chromeos_vbc_read(void *buf, size_t count)
-{
-       if (!nv_size) {
-               pr_err("%s nonvolatile context not configured!\n", __func__);
-               return -ENODEV;
-       }
-
-       if (count < nv_size) {
-               pr_err("not enough room to read vboot context (%zd < %d)\n",
-                      count, nv_size);
-               return -ENOSPC;
-       }
-
-       return vbc_blk_read(buf);
-}
-
-ssize_t chromeos_vbc_write(const void *buf, size_t count)
-{
-       if (!nv_size) {
-               pr_err("%s nonvolatile context not configured!\n", __func__);
-               return -ENODEV;
-       }
-
-       if (count != nv_size) {
-               pr_err("wrong write buffer size (%zd != %d)\n",
-                      count, nv_size);
-               return -ENOSPC;
-       }
-
-       return vbc_blk_write(buf);
-}
-
-static int __devinit chromeos_arm_platform_gpio(struct platform_device *pdev)
+static int __devinit chromeos_arm_probe(struct platform_device *pdev)
 {
        int gpio, err, active_low;
        enum of_gpio_flags flags;
        struct device_node *np = pdev->dev.of_node;
 
-       if (!np)
-               return -ENODEV;
+       if (!np) {
+               err = -ENODEV;
+               goto err;
+       }
 
        gpio = of_get_named_gpio_flags(np, "write-protect-gpio", 0, &flags);
        if (!gpio_is_valid(gpio)) {
                dev_err(&pdev->dev, "invalid write-protect gpio descriptor\n");
-               return -EINVAL;
+               err = -EINVAL;
+               goto err;
        }
 
        active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
 
        err = gpio_request_one(gpio, GPIOF_DIR_IN, "firmware-write-protect");
        if (err)
-               return err;
+               goto err;
        err = gpio_sysfs_set_active_low(gpio, active_low);
        if (err)
-               return err;
+               goto err;
        gpio_export(gpio, 0);
        gpio_export_link(&pdev->dev, "write-protect", gpio);
 
-       return 0;
-}
-
-static int __devinit chromeos_arm_probe(struct platform_device *pdev)
-{
-       int proplen, err;
-       const int *prop;
-       struct device_node *fw_dn = pdev->dev.of_node;
-
-       err = chromeos_arm_platform_gpio(pdev);
-       if (err)
-               goto err;
-
-       prop = of_get_property(fw_dn, "nonvolatile-context-offset", &proplen);
-       if (!prop || proplen != 4) {
-               dev_err(&pdev->dev, "missing nonvolatile memory offset\n");
-               err = -ENODEV;
-               goto err;
-       }
-       nv_offset = be32_to_cpup(prop);
-
-       prop = of_get_property(fw_dn, "nonvolatile-context-size", &proplen);
-       if (!prop || proplen != 4) {
-               dev_err(&pdev->dev, "missing size of nonvolatile memory\n");
-               err = -ENODEV;
-               goto err;
-       }
-       nv_size = be32_to_cpup(prop);
-
-       if ((nv_offset + nv_size > SECTOR_SIZE) ||
-           (nv_size > MAX_VBOOT_CONTEXT_BUFFER_SIZE)) {
-               /* vboot context block won't fit into a sector */
-               dev_err(&pdev->dev, "bad vboot context location: %d:%d!\n",
-                       nv_offset, nv_size);
-               err = -EINVAL;
-               goto err;
-       }
-
-       prop = of_get_property(fw_dn, "nonvolatile-context-lba", &proplen);
-       if (!prop) {
-               dev_err(&pdev->dev, "missing nvcontext lba\n");
-               err = -ENODEV;
-               goto err;
-       }
-       switch (proplen) {
-       case 4:
-               nv_lba = be32_to_cpup(prop);
-               break;
-       case 8:
-               nv_lba = be64_to_cpup((const __be64 *)prop);
-               break;
-       default:
-               dev_err(&pdev->dev, "invalid nvcontext lba\n");
-               err = -EINVAL;
-               goto err;
-       }
-
-       /* XXXOJN FIXME: There should be a search for the right block device to
-        * use for volatile storage here, not just assume mmcblk0. This should
-        * include comparing the cached context passed in through a property.
-        */
-
        dev_info(&pdev->dev, "chromeos system detected\n");
 
        err = 0;
 err:
-       of_node_put(fw_dn);
+       of_node_put(np);
 
        return err;
 }
diff --git a/drivers/platform/chromeos_vbc_blk.c b/drivers/platform/chromeos_vbc_blk.c
new file mode 100644 (file)
index 0000000..5a09ddd
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ *  Copyright (C) 2012 The Chromium OS Authors
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define pr_fmt(fmt) "chromeos_vbc_blk: " fmt
+
+#include <linux/ide.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "chromeos.h"
+
+static struct {
+       bool initialized;
+       phandle phandle;
+       u64 lba;
+       u16 offset, size;
+} config;
+
+static int match_of_node(struct device *dev, void *data)
+{
+       return dev->of_node == data;
+}
+
+static struct block_device *vbc_blk_get_device(phandle phandle, fmode_t devmode)
+{
+       struct device_node *dn;
+       struct device *dev;
+
+       dn = of_find_node_by_phandle(phandle);
+       if (!dn)
+               return ERR_PTR(-ENODEV);
+
+       dev = bus_find_device(&platform_bus_type, NULL, dn, match_of_node);
+       if (!dev)
+               return ERR_PTR(-ENODEV);
+
+       /*
+        * TODO(chrome-os-partner:16441): Search block_device from the dev
+        * struct we just found instead of hard-coding major and minor here.
+        */
+       return blkdev_get_by_dev(MKDEV(MMC_BLOCK_MAJOR, 0), devmode, NULL);
+}
+
+static void vbc_blk_endio(struct bio *bio, int err)
+{
+       struct completion *c = bio->bi_private;
+       bio->bi_private = (void *)err;
+       complete(c);
+}
+
+static void vbc_blk_submit_bio(struct bio *bio, int rq)
+{
+       DECLARE_COMPLETION_ONSTACK(wait);
+
+       bio->bi_end_io  = vbc_blk_endio;
+       bio->bi_private = &wait;
+       submit_bio(rq, bio);
+       wait_for_completion(&wait);
+}
+
+static int vbc_blk_access(struct page *page, sector_t sector, bool is_read)
+{
+       struct block_device *bdev;
+       struct bio *bio;
+       int err, rq;
+       fmode_t devmode = is_read ? FMODE_READ : FMODE_WRITE;
+
+       bdev = vbc_blk_get_device(config.phandle, devmode);
+       if (IS_ERR(bdev)) {
+               pr_err("could not open block dev\n");
+               return PTR_ERR(bdev);
+       }
+
+       /* map the sector to page */
+       bio = bio_alloc(GFP_NOIO, 1);
+       if (!bio) {
+               err = -ENOMEM;
+               goto unwind_bdev;
+       }
+       bio->bi_bdev    = bdev;
+       bio->bi_sector  = sector;
+       bio->bi_vcnt    = 1;
+       bio->bi_idx     = 0;
+       bio->bi_size    = SECTOR_SIZE;
+       bio->bi_io_vec[0].bv_page       = page;
+       bio->bi_io_vec[0].bv_len        = SECTOR_SIZE;
+       bio->bi_io_vec[0].bv_offset     = 0;
+
+       /* submit bio */
+       rq = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE;
+       if (!is_read)
+               rq |= REQ_WRITE;
+
+       vbc_blk_submit_bio(bio, rq);
+
+       /* vbc_blk_endio passes up any error in bi_private */
+       err = (int)bio->bi_private;
+       bio_put(bio);
+
+unwind_bdev:
+       if (!is_read) {
+               fsync_bdev(bdev);
+               invalidate_bdev(bdev);
+       }
+       blkdev_put(bdev, devmode);
+
+       return err;
+}
+
+/*
+ * This function accepts a buffer with exactly config.size bytes. It reads the
+ * appropriate mmc one sector block, extacts the nonvolatile data from there
+ * and copies it to the provided buffer.
+ */
+static int vbc_blk_read(u8 *data)
+{
+       struct page *page;
+       char *virtual_addr;
+       int ret;
+
+       page = alloc_page(GFP_NOIO);
+       if (!page) {
+               pr_err("page allocation failed\n");
+               return -ENOMEM;
+       }
+
+       virtual_addr = page_address(page);
+       if (!virtual_addr) {
+               pr_err("page not mapped!\n");
+               __free_page(page);
+               return -EFAULT;
+       }
+
+       ret = vbc_blk_access(page, config.lba, 1);
+       if (ret)
+               goto out;
+
+       memcpy(data, virtual_addr + config.offset, config.size);
+       ret = config.size;
+
+out:
+       __free_page(page);
+       return ret;
+}
+
+/*
+ * This function accepts a buffer with exactly config.size bytes. It reads the
+ * appropriate mmc one sector block, blends in the new vboot context contents
+ * and then writes the sector back.
+ */
+static int vbc_blk_write(const u8 *data)
+{
+       struct page *page;
+       char *virtual_addr;
+       int ret;
+
+       page = alloc_page(GFP_NOIO);
+       if (!page) {
+               pr_err("page allocation failed\n");
+               return -ENOMEM;
+       }
+
+       virtual_addr = page_address(page);
+       if (!virtual_addr) {
+               pr_err("page not mapped!\n");
+               __free_page(page);
+               return -EFAULT;
+       }
+
+       ret = vbc_blk_access(page, config.lba, 1);
+       if (ret)
+               goto out;
+
+       /*
+        * Sector has been read, lets blend in vboot context data and write
+        * the sector back.
+        */
+       memcpy(virtual_addr + config.offset, data, config.size);
+
+       ret = vbc_blk_access(page, config.lba, 0);
+       if (!ret)
+               ret = config.size;
+
+out:
+       __free_page(page);
+       return ret;
+}
+
+static int vbc_blk_init(void)
+{
+       struct device_node *fw_dn;
+       int err;
+       u32 prop[4];
+
+       fw_dn = of_find_compatible_node(NULL, NULL, "chromeos-firmware");
+       if (!fw_dn) {
+               pr_err("missing chromeos-firmware device node\n");
+               err = -ENODEV;
+               goto err;
+       }
+
+       err = of_property_read_u32_array(fw_dn, "chromeos-vbc-blk", prop,
+                       sizeof(prop) / sizeof(prop[0]));
+       if (err) {
+               pr_err("missing chromeos-vbc-blk property\n");
+               goto err;
+       }
+
+       config.phandle = prop[0];
+       config.lba = prop[1];
+       config.offset = prop[2];
+       config.size = prop[3];
+
+       if ((config.offset + config.size > SECTOR_SIZE) ||
+           (config.size > MAX_VBOOT_CONTEXT_BUFFER_SIZE)) {
+               /* vboot context block won't fit into a sector */
+               pr_err("bad vboot context location: %d:%d!\n",
+                      config.offset, config.size);
+               err = -EINVAL;
+               goto err;
+       }
+
+       config.initialized = true;
+       err = 0;
+err:
+       of_node_put(fw_dn);
+
+       return err;
+}
+
+ssize_t chromeos_vbc_read(void *buf, size_t count)
+{
+       int err;
+
+       if (!config.initialized) {
+               err = vbc_blk_init();
+               if (err < 0)
+                       return err;
+       }
+
+       if (count < config.size) {
+               pr_err("not enough room to read vboot context (%zd < %d)\n",
+                      count, config.size);
+               return -ENOSPC;
+       }
+
+       return vbc_blk_read(buf);
+}
+
+ssize_t chromeos_vbc_write(const void *buf, size_t count)
+{
+       int err;
+
+       if (!config.initialized) {
+               err = vbc_blk_init();
+               if (err < 0)
+                       return err;
+       }
+
+       if (count != config.size) {
+               pr_err("wrong write buffer size (%zd != %d)\n",
+                      count, config.size);
+               return -ENOSPC;
+       }
+
+       return vbc_blk_write(buf);
+}