xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c (revision b2f1e6301efa4a80becdb0715416c3cbc693fbb4)
1 /*
2  * Copyright 2012 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  */
23 #include "priv.h"
24 
25 #include <subdev/pci.h>
26 
27 #define NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE 0x4947564E	/* "NVGI" */
28 #define NV_ROM_DIRECTORY_IDENTIFIER 0x44524652			/* "RFRD" */
29 
30 struct priv {
31 	struct nvkm_device *device;
32 	u32 pci_rom_offset;
33 };
34 
35 static u32
36 nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
37 {
38 	struct priv *priv = data;
39 	struct nvkm_device *device = priv->device;
40 	u32 i;
41 
42 	/* Make sure we don't try to read past the end of data[] */
43 	if (offset + length > bios->size)
44 		return 0;
45 
46 	/* Make sure the read falls within the 1MB PROM window */
47 	if (offset + priv->pci_rom_offset + length > 0x00100000)
48 		return 0;
49 
50 	for (i = offset; i < offset + length; i += 4)
51 		*(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset + i);
52 	return length;
53 }
54 
55 static void
56 nvbios_prom_fini(void *data)
57 {
58 	struct priv *priv = data;
59 	struct nvkm_device *device = priv->device;
60 
61 	nvkm_pci_rom_shadow(device->pci, true);
62 
63 	kfree(data);
64 }
65 
66 static void *
67 nvbios_prom_init(struct nvkm_bios *bios, const char *name)
68 {
69 	struct nvkm_device *device = bios->subdev.device;
70 	struct priv *priv;
71 	u32 fixed0;
72 
73 	/* There is no PROM on NV4x iGPUs */
74 	if (device->card_type == NV_40 && device->chipset >= 0x4c)
75 		return ERR_PTR(-ENODEV);
76 
77 	priv = kzalloc_obj(*priv);
78 	if (!priv)
79 		return ERR_PTR(-ENOMEM);
80 
81 	/* Disable the PCI ROM shadow so that we can read PROM. */
82 	nvkm_pci_rom_shadow(device->pci, false);
83 
84 	/*
85 	 * Check for an IFR header. If present, parse it to find the actual PCI ROM header.
86 	 *
87 	 * The IFR header is documented in Documentation/gpu/nova/core/vbios.rst
88 	 */
89 	fixed0 = nvkm_rd32(device, 0x300000);
90 	if (fixed0 == NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE) {
91 		u32 fixed1 = nvkm_rd32(device, 0x300004);
92 		u8 version = (fixed1 >> 8) & 0xff;
93 		u32 fixed2, data_size, offset, signature;
94 
95 		switch (version) {
96 		case 1:
97 		case 2:
98 			data_size = (fixed1 >> 16) & 0x7fff;
99 			priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + data_size + 4);
100 			break;
101 		case 3:
102 			fixed2 = nvkm_rd32(device, 0x300008);
103 			data_size = fixed2 & 0x000fffff;
104 
105 			/* ROM directory offset */
106 			offset = nvkm_rd32(device, 0x300000 + data_size) + 4096;
107 
108 			signature = nvkm_rd32(device, 0x300000 + offset);
109 			if (signature != NV_ROM_DIRECTORY_IDENTIFIER) {
110 				nvkm_error(&bios->subdev, "could not find IFR ROM directory\n");
111 				goto fail;
112 			}
113 
114 			priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + offset + 8);
115 
116 			break;
117 		default:
118 			nvkm_error(&bios->subdev, "unsupported IFR header version %u\n",
119 				   version);
120 			goto fail;
121 		}
122 
123 		/* Double-check that the offset is valid */
124 		if (priv->pci_rom_offset >= 0x00100000) {
125 			nvkm_error(&bios->subdev,
126 				   "PCI ROM offset of 0x%x is too large\n", priv->pci_rom_offset);
127 			goto fail;
128 		}
129 
130 		/* If there is an IFR header, there must also be a PCI ROM header. */
131 		signature = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset) & 0xffff;
132 		if (signature != 0xaa55) {
133 			nvkm_error(&bios->subdev,
134 				   "could not find PCI ROM signature at offset 0x%x\n",
135 				   priv->pci_rom_offset);
136 			goto fail;
137 		}
138 	}
139 
140 	priv->device = device;
141 	return priv;
142 
143 fail:
144 	nvkm_pci_rom_shadow(device->pci, true);
145 	kfree(priv);
146 	return ERR_PTR(-ENODEV);
147 }
148 
149 const struct nvbios_source
150 nvbios_prom = {
151 	.name = "PROM",
152 	.init = nvbios_prom_init,
153 	.fini = nvbios_prom_fini,
154 	.read = nvbios_prom_read,
155 	.rw = false,
156 };
157