| /* |
| * 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; |
| } |