diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c new file mode 100644 index 000000000000..520facf9bc07 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "priv.h" +#include <subdev/timer.h> + +static const char * +managed_falcons_names[] = { + [NVKM_SECBOOT_FALCON_PMU] = "PMU", + [NVKM_SECBOOT_FALCON_RESERVED] = "<reserved>", + [NVKM_SECBOOT_FALCON_FECS] = "FECS", + [NVKM_SECBOOT_FALCON_GPCCS] = "GPCCS", + [NVKM_SECBOOT_FALCON_END] = "<invalid>", +}; + +/* + * Helper falcon functions + */ + +static int +falcon_clear_halt_interrupt(struct nvkm_device *device, u32 base) +{ + int ret; + + /* clear halt interrupt */ + nvkm_mask(device, base + 0x004, 0x10, 0x10); + /* wait until halt interrupt is cleared */ + ret = nvkm_wait_msec(device, 10, base + 0x008, 0x10, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int +falcon_wait_idle(struct nvkm_device *device, u32 base) +{ + int ret; + + ret = nvkm_wait_msec(device, 10, base + 0x04c, 0xffff, 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int +nvkm_secboot_falcon_enable(struct nvkm_secboot *sb) +{ + struct nvkm_device *device = sb->subdev.device; + int ret; + + /* enable engine */ + nvkm_mask(device, 0x200, sb->enable_mask, sb->enable_mask); + nvkm_rd32(device, 0x200); + ret = nvkm_wait_msec(device, 10, sb->base + 0x10c, 0x6, 0x0); + if (ret < 0) { + nvkm_mask(device, 0x200, sb->enable_mask, 0x0); + nvkm_error(&sb->subdev, "Falcon mem scrubbing timeout\n"); + return ret; + } + + ret = falcon_wait_idle(device, sb->base); + if (ret) + return ret; + + /* enable IRQs */ + nvkm_wr32(device, sb->base + 0x010, 0xff); + nvkm_mask(device, 0x640, sb->irq_mask, sb->irq_mask); + nvkm_mask(device, 0x644, sb->irq_mask, sb->irq_mask); + + return 0; +} + +static int +nvkm_secboot_falcon_disable(struct nvkm_secboot *sb) +{ + struct nvkm_device *device = sb->subdev.device; + + /* disable IRQs and wait for any previous code to complete */ + nvkm_mask(device, 0x644, sb->irq_mask, 0x0); + nvkm_mask(device, 0x640, sb->irq_mask, 0x0); + nvkm_wr32(device, sb->base + 0x014, 0xff); + + falcon_wait_idle(device, sb->base); + + /* disable engine */ + nvkm_mask(device, 0x200, sb->enable_mask, 0x0); + + return 0; +} + +int +nvkm_secboot_falcon_reset(struct nvkm_secboot *sb) +{ + int ret; + + ret = nvkm_secboot_falcon_disable(sb); + if (ret) + return ret; + + ret = nvkm_secboot_falcon_enable(sb); + if (ret) + return ret; + + return 0; +} + +/** + * nvkm_secboot_falcon_run - run the falcon that will perform secure boot + * + * This function is to be called after all chip-specific preparations have + * been completed. It will start the falcon to perform secure boot, wait for + * it to halt, and report if an error occurred. + */ +int +nvkm_secboot_falcon_run(struct nvkm_secboot *sb) +{ + struct nvkm_device *device = sb->subdev.device; + int ret; + + /* Start falcon */ + nvkm_wr32(device, sb->base + 0x100, 0x2); + + /* Wait for falcon halt */ + ret = nvkm_wait_msec(device, 100, sb->base + 0x100, 0x10, 0x10); + if (ret < 0) + return ret; + + /* If mailbox register contains an error code, then ACR has failed */ + ret = nvkm_rd32(device, sb->base + 0x040); + if (ret) { + nvkm_error(&sb->subdev, "ACR boot failed, ret 0x%08x", ret); + falcon_clear_halt_interrupt(device, sb->base); + return -EINVAL; + } + + return 0; +} + + +/** + * nvkm_secboot_reset() - reset specified falcon + */ +int +nvkm_secboot_reset(struct nvkm_secboot *sb, u32 falcon) +{ + /* Unmanaged falcon? */ + if (!(BIT(falcon) & sb->func->managed_falcons)) { + nvkm_error(&sb->subdev, "cannot reset unmanaged falcon!\n"); + return -EINVAL; + } + + return sb->func->reset(sb, falcon); +} + +/** + * nvkm_secboot_start() - start specified falcon + */ +int +nvkm_secboot_start(struct nvkm_secboot *sb, u32 falcon) +{ + /* Unmanaged falcon? */ + if (!(BIT(falcon) & sb->func->managed_falcons)) { + nvkm_error(&sb->subdev, "cannot start unmanaged falcon!\n"); + return -EINVAL; + } + + return sb->func->start(sb, falcon); +} + +/** + * nvkm_secboot_is_managed() - check whether a given falcon is securely-managed + */ +bool +nvkm_secboot_is_managed(struct nvkm_secboot *secboot, + enum nvkm_secboot_falcon fid) +{ + if (!secboot) + return false; + + return secboot->func->managed_falcons & BIT(fid); +} + +static int +nvkm_secboot_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_secboot *sb = nvkm_secboot(subdev); + int ret = 0; + + /* Call chip-specific init function */ + if (sb->func->init) + ret = sb->func->init(sb); + if (ret) { + nvkm_error(subdev, "Secure Boot initialization failed: %d\n", + ret); + return ret; + } + + /* + * Build all blobs - the same blobs can be used to perform secure boot + * multiple times + */ + if (sb->func->prepare_blobs) + ret = sb->func->prepare_blobs(sb); + + return ret; +} + +static int +nvkm_secboot_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_secboot *sb = nvkm_secboot(subdev); + int ret = 0; + + if (sb->func->fini) + ret = sb->func->fini(sb, suspend); + + return ret; +} + +static void * +nvkm_secboot_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_secboot *sb = nvkm_secboot(subdev); + void *ret = NULL; + + if (sb->func->dtor) + ret = sb->func->dtor(sb); + + return ret; +} + +static const struct nvkm_subdev_func +nvkm_secboot = { + .oneinit = nvkm_secboot_oneinit, + .fini = nvkm_secboot_fini, + .dtor = nvkm_secboot_dtor, +}; + +int +nvkm_secboot_ctor(const struct nvkm_secboot_func *func, + struct nvkm_device *device, int index, + struct nvkm_secboot *sb) +{ + unsigned long fid; + + nvkm_subdev_ctor(&nvkm_secboot, device, index, 0, &sb->subdev); + sb->func = func; + + /* setup the performing falcon's base address and masks */ + switch (func->boot_falcon) { + case NVKM_SECBOOT_FALCON_PMU: + sb->base = 0x10a000; + sb->irq_mask = 0x1000000; + sb->enable_mask = 0x2000; + break; + default: + nvkm_error(&sb->subdev, "invalid secure boot falcon\n"); + return -EINVAL; + }; + + nvkm_debug(&sb->subdev, "securely managed falcons:\n"); + for_each_set_bit(fid, &sb->func->managed_falcons, + NVKM_SECBOOT_FALCON_END) + nvkm_debug(&sb->subdev, "- %s\n", managed_falcons_names[fid]); + + return 0; +} |