cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

shadow.c (6206B)


      1/*
      2 * Copyright 2014 Red Hat Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 * OTHER DEALINGS IN THE SOFTWARE.
     21 *
     22 * Authors: Ben Skeggs <bskeggs@redhat.com>
     23 */
     24#include "priv.h"
     25
     26#include <core/option.h>
     27#include <subdev/bios.h>
     28#include <subdev/bios/image.h>
     29
     30struct shadow {
     31	u32 skip;
     32	const struct nvbios_source *func;
     33	void *data;
     34	u32 size;
     35	int score;
     36};
     37
     38static bool
     39shadow_fetch(struct nvkm_bios *bios, struct shadow *mthd, u32 upto)
     40{
     41	const u32 limit = (upto + 3) & ~3;
     42	const u32 start = bios->size;
     43	void *data = mthd->data;
     44	if (nvbios_extend(bios, limit) > 0) {
     45		u32 read = mthd->func->read(data, start, limit - start, bios);
     46		bios->size = start + read;
     47	}
     48	return bios->size >= upto;
     49}
     50
     51static int
     52shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
     53{
     54	struct nvkm_subdev *subdev = &bios->subdev;
     55	struct nvbios_image image;
     56	int score = 1;
     57
     58	if (mthd->func->no_pcir) {
     59		image.base = 0;
     60		image.type = 0;
     61		image.size = mthd->func->size(mthd->data);
     62		image.last = 1;
     63	} else {
     64		if (!shadow_fetch(bios, mthd, offset + 0x1000)) {
     65			nvkm_debug(subdev, "%08x: header fetch failed\n",
     66				   offset);
     67			return 0;
     68		}
     69
     70		if (!nvbios_image(bios, idx, &image)) {
     71			nvkm_debug(subdev, "image %d invalid\n", idx);
     72			return 0;
     73		}
     74	}
     75	nvkm_debug(subdev, "%08x: type %02x, %d bytes\n",
     76		   image.base, image.type, image.size);
     77
     78	if (!shadow_fetch(bios, mthd, image.base + image.size)) {
     79		nvkm_debug(subdev, "%08x: fetch failed\n", image.base);
     80		return 0;
     81	}
     82
     83	switch (image.type) {
     84	case 0x00:
     85		if (!mthd->func->ignore_checksum &&
     86		    nvbios_checksum(&bios->data[image.base], image.size)) {
     87			nvkm_debug(subdev, "%08x: checksum failed\n",
     88				   image.base);
     89			if (!mthd->func->require_checksum) {
     90				if (mthd->func->rw)
     91					score += 1;
     92				score += 1;
     93			} else
     94				return 0;
     95		} else {
     96			score += 3;
     97		}
     98		break;
     99	default:
    100		score += 3;
    101		break;
    102	}
    103
    104	if (!image.last)
    105		score += shadow_image(bios, idx + 1, offset + image.size, mthd);
    106	return score;
    107}
    108
    109static int
    110shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name)
    111{
    112	const struct nvbios_source *func = mthd->func;
    113	struct nvkm_subdev *subdev = &bios->subdev;
    114	if (func->name) {
    115		nvkm_debug(subdev, "trying %s...\n", name ? name : func->name);
    116		if (func->init) {
    117			mthd->data = func->init(bios, name);
    118			if (IS_ERR(mthd->data)) {
    119				mthd->data = NULL;
    120				return 0;
    121			}
    122		}
    123		mthd->score = shadow_image(bios, 0, 0, mthd);
    124		if (func->fini)
    125			func->fini(mthd->data);
    126		nvkm_debug(subdev, "scored %d\n", mthd->score);
    127		mthd->data = bios->data;
    128		mthd->size = bios->size;
    129		bios->data  = NULL;
    130		bios->size  = 0;
    131	}
    132	return mthd->score;
    133}
    134
    135static u32
    136shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
    137{
    138	const struct firmware *fw = data;
    139	if (offset + length <= fw->size) {
    140		memcpy(bios->data + offset, fw->data + offset, length);
    141		return length;
    142	}
    143	return 0;
    144}
    145
    146static void *
    147shadow_fw_init(struct nvkm_bios *bios, const char *name)
    148{
    149	struct device *dev = bios->subdev.device->dev;
    150	const struct firmware *fw;
    151	int ret = request_firmware(&fw, name, dev);
    152	if (ret)
    153		return ERR_PTR(-ENOENT);
    154	return (void *)fw;
    155}
    156
    157static const struct nvbios_source
    158shadow_fw = {
    159	.name = "firmware",
    160	.init = shadow_fw_init,
    161	.fini = (void(*)(void *))release_firmware,
    162	.read = shadow_fw_read,
    163	.rw = false,
    164};
    165
    166int
    167nvbios_shadow(struct nvkm_bios *bios)
    168{
    169	struct nvkm_subdev *subdev = &bios->subdev;
    170	struct nvkm_device *device = subdev->device;
    171	struct shadow mthds[] = {
    172		{ 0, &nvbios_of },
    173		{ 0, &nvbios_ramin },
    174		{ 0, &nvbios_rom },
    175		{ 0, &nvbios_acpi_fast },
    176		{ 4, &nvbios_acpi_slow },
    177		{ 1, &nvbios_pcirom },
    178		{ 1, &nvbios_platform },
    179		{}
    180	}, *mthd, *best = NULL;
    181	const char *optarg;
    182	char *source;
    183	int optlen;
    184
    185	/* handle user-specified bios source */
    186	optarg = nvkm_stropt(device->cfgopt, "NvBios", &optlen);
    187	source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
    188	if (source) {
    189		/* try to match one of the built-in methods */
    190		for (mthd = mthds; mthd->func; mthd++) {
    191			if (mthd->func->name &&
    192			    !strcasecmp(source, mthd->func->name)) {
    193				best = mthd;
    194				if (shadow_method(bios, mthd, NULL))
    195					break;
    196			}
    197		}
    198
    199		/* otherwise, attempt to load as firmware */
    200		if (!best && (best = mthd)) {
    201			mthd->func = &shadow_fw;
    202			shadow_method(bios, mthd, source);
    203			mthd->func = NULL;
    204		}
    205
    206		if (!best->score) {
    207			nvkm_error(subdev, "%s invalid\n", source);
    208			kfree(source);
    209			source = NULL;
    210		}
    211	}
    212
    213	/* scan all potential bios sources, looking for best image */
    214	if (!best || !best->score) {
    215		for (mthd = mthds, best = mthd; mthd->func; mthd++) {
    216			if (!mthd->skip || best->score < mthd->skip) {
    217				if (shadow_method(bios, mthd, NULL)) {
    218					if (mthd->score > best->score)
    219						best = mthd;
    220				}
    221			}
    222		}
    223	}
    224
    225	/* cleanup the ones we didn't use */
    226	for (mthd = mthds; mthd->func; mthd++) {
    227		if (mthd != best)
    228			kfree(mthd->data);
    229	}
    230
    231	if (!best->score) {
    232		nvkm_error(subdev, "unable to locate usable image\n");
    233		return -EINVAL;
    234	}
    235
    236	nvkm_debug(subdev, "using image from %s\n", best->func ?
    237		   best->func->name : source);
    238	bios->data = best->data;
    239	bios->size = best->size;
    240	kfree(source);
    241	return 0;
    242}