Skip to content

Commit 4489b98

Browse files
author
Ben Skeggs
committed
drm/nouveau/bios: rework vbios shadowing
Refactored to allow shadowing of VBIOS images longer than 64KiB, which allows us to pass the VBIOS checksum test on certain boards. There's also a workaround for reading the PROM VBIOS on some chipsets. Signed-off-by: Ben Skeggs <[email protected]>
1 parent 05a7c15 commit 4489b98

File tree

2 files changed

+158
-121
lines changed

2 files changed

+158
-121
lines changed

drivers/gpu/drm/nouveau/nouveau_bios.c

Lines changed: 156 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -65,195 +65,232 @@ static bool nv_cksum(const uint8_t *data, unsigned int length)
6565
}
6666

6767
static int
68-
score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
68+
score_vbios(struct nvbios *bios, const bool writeable)
6969
{
70-
if (!(data[0] == 0x55 && data[1] == 0xAA)) {
71-
NV_TRACEWARN(dev, "... BIOS signature not found\n");
70+
if (!bios->data || bios->data[0] != 0x55 || bios->data[1] != 0xAA) {
71+
NV_TRACEWARN(bios->dev, "... BIOS signature not found\n");
7272
return 0;
7373
}
7474

75-
if (nv_cksum(data, data[2] * 512)) {
76-
NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
75+
if (nv_cksum(bios->data, bios->data[2] * 512)) {
76+
NV_TRACEWARN(bios->dev, "... BIOS checksum invalid\n");
7777
/* if a ro image is somewhat bad, it's probably all rubbish */
7878
return writeable ? 2 : 1;
79-
} else
80-
NV_TRACE(dev, "... appears to be valid\n");
79+
}
8180

81+
NV_TRACE(bios->dev, "... appears to be valid\n");
8282
return 3;
8383
}
8484

85-
static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
85+
static void
86+
bios_shadow_prom(struct nvbios *bios)
8687
{
88+
struct drm_device *dev = bios->dev;
8789
struct drm_nouveau_private *dev_priv = dev->dev_private;
88-
uint32_t pci_nv_20, save_pci_nv_20;
89-
int pcir_ptr;
90+
u32 pcireg, access;
91+
u16 pcir;
9092
int i;
9193

94+
/* enable access to rom */
9295
if (dev_priv->card_type >= NV_50)
93-
pci_nv_20 = 0x88050;
96+
pcireg = 0x088050;
9497
else
95-
pci_nv_20 = NV_PBUS_PCI_NV_20;
98+
pcireg = NV_PBUS_PCI_NV_20;
99+
access = nv_mask(dev, pcireg, 0x00000001, 0x00000000);
96100

97-
/* enable ROM access */
98-
save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
99-
nvWriteMC(dev, pci_nv_20,
100-
save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
101+
/* bail if no rom signature, with a workaround for a PROM reading
102+
* issue on some chipsets. the first read after a period of
103+
* inactivity returns the wrong result, so retry the first header
104+
* byte a few times before giving up as a workaround
105+
*/
106+
i = 16;
107+
do {
108+
if (nv_rd08(dev, NV_PROM_OFFSET + 0) == 0x55)
109+
break;
110+
} while (i--);
101111

102-
/* bail if no rom signature */
103-
if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
104-
nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
112+
if (!i || nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
105113
goto out;
106114

107115
/* additional check (see note below) - read PCI record header */
108-
pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
109-
nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
110-
if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
111-
nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
112-
nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
113-
nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
116+
pcir = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
117+
nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
118+
if (nv_rd08(dev, NV_PROM_OFFSET + pcir + 0) != 'P' ||
119+
nv_rd08(dev, NV_PROM_OFFSET + pcir + 1) != 'C' ||
120+
nv_rd08(dev, NV_PROM_OFFSET + pcir + 2) != 'I' ||
121+
nv_rd08(dev, NV_PROM_OFFSET + pcir + 3) != 'R')
114122
goto out;
115123

116-
/* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a
117-
* a good read may be obtained by waiting or re-reading (cargocult: 5x)
118-
* each byte. we'll hope pramin has something usable instead
119-
*/
120-
for (i = 0; i < NV_PROM_SIZE; i++)
121-
data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
124+
/* read entire bios image to system memory */
125+
bios->length = nv_rd08(dev, NV_PROM_OFFSET + 2) * 512;
126+
bios->data = kmalloc(bios->length, GFP_KERNEL);
127+
if (bios->data) {
128+
for (i = 0; i < bios->length; i++)
129+
bios->data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
130+
}
122131

123132
out:
124-
/* disable ROM access */
125-
nvWriteMC(dev, pci_nv_20,
126-
save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
133+
/* disable access to rom */
134+
nv_wr32(dev, pcireg, access);
127135
}
128136

129-
static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
137+
static void
138+
bios_shadow_pramin(struct nvbios *bios)
130139
{
140+
struct drm_device *dev = bios->dev;
131141
struct drm_nouveau_private *dev_priv = dev->dev_private;
132-
uint32_t old_bar0_pramin = 0;
142+
u32 bar0 = 0;
133143
int i;
134144

135145
if (dev_priv->card_type >= NV_50) {
136146
u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8;
137147
if (!addr) {
138-
addr = (u64)nv_rd32(dev, 0x1700) << 16;
148+
addr = (u64)nv_rd32(dev, 0x001700) << 16;
139149
addr += 0xf0000;
140150
}
141151

142-
old_bar0_pramin = nv_rd32(dev, 0x1700);
143-
nv_wr32(dev, 0x1700, addr >> 16);
152+
bar0 = nv_mask(dev, 0x001700, 0xffffffff, addr >> 16);
144153
}
145154

146155
/* bail if no rom signature */
147-
if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
156+
if (nv_rd08(dev, NV_PRAMIN_OFFSET + 0) != 0x55 ||
148157
nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
149158
goto out;
150159

151-
for (i = 0; i < NV_PROM_SIZE; i++)
152-
data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
160+
bios->length = nv_rd08(dev, NV_PRAMIN_OFFSET + 2) * 512;
161+
bios->data = kmalloc(bios->length, GFP_KERNEL);
162+
if (bios->data) {
163+
for (i = 0; i < bios->length; i++)
164+
bios->data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
165+
}
153166

154167
out:
155168
if (dev_priv->card_type >= NV_50)
156-
nv_wr32(dev, 0x1700, old_bar0_pramin);
169+
nv_wr32(dev, 0x001700, bar0);
157170
}
158171

159-
static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
172+
static void
173+
bios_shadow_pci(struct nvbios *bios)
174+
{
175+
struct pci_dev *pdev = bios->dev->pdev;
176+
size_t length;
177+
178+
if (!pci_enable_rom(pdev)) {
179+
void __iomem *rom = pci_map_rom(pdev, &length);
180+
if (rom) {
181+
bios->data = kmalloc(length, GFP_KERNEL);
182+
if (bios->data) {
183+
memcpy_fromio(bios->data, rom, length);
184+
bios->length = length;
185+
}
186+
pci_unmap_rom(pdev, rom);
187+
}
188+
189+
pci_disable_rom(pdev);
190+
}
191+
}
192+
193+
static void
194+
bios_shadow_acpi(struct nvbios *bios)
160195
{
161-
void __iomem *rom = NULL;
162-
size_t rom_len;
163-
int ret;
196+
struct pci_dev *pdev = bios->dev->pdev;
197+
int ptr, len, ret;
198+
u8 data[3];
164199

165-
ret = pci_enable_rom(dev->pdev);
166-
if (ret)
200+
if (!nouveau_acpi_rom_supported(pdev))
167201
return;
168202

169-
rom = pci_map_rom(dev->pdev, &rom_len);
170-
if (!rom)
171-
goto out;
172-
memcpy_fromio(data, rom, rom_len);
173-
pci_unmap_rom(dev->pdev, rom);
203+
ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data));
204+
if (ret != sizeof(data))
205+
return;
174206

175-
out:
176-
pci_disable_rom(dev->pdev);
177-
}
207+
bios->length = min(data[2] * 512, 65536);
208+
bios->data = kmalloc(bios->length, GFP_KERNEL);
209+
if (!bios->data)
210+
return;
178211

179-
static void load_vbios_acpi(struct drm_device *dev, uint8_t *data)
180-
{
181-
int i;
182-
int ret;
183-
int size = 64 * 1024;
212+
len = bios->length;
213+
ptr = 0;
214+
while (len) {
215+
int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len;
184216

185-
if (!nouveau_acpi_rom_supported(dev->pdev))
186-
return;
217+
ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size);
218+
if (ret != size) {
219+
kfree(bios->data);
220+
bios->data = NULL;
221+
return;
222+
}
187223

188-
for (i = 0; i < (size / ROM_BIOS_PAGE); i++) {
189-
ret = nouveau_acpi_get_bios_chunk(data,
190-
(i * ROM_BIOS_PAGE),
191-
ROM_BIOS_PAGE);
192-
if (ret <= 0)
193-
break;
224+
len -= size;
225+
ptr += size;
194226
}
195-
return;
196227
}
197228

198229
struct methods {
199230
const char desc[8];
200-
void (*loadbios)(struct drm_device *, uint8_t *);
231+
void (*shadow)(struct nvbios *);
201232
const bool rw;
233+
int score;
234+
u32 size;
235+
u8 *data;
202236
};
203237

204-
static struct methods shadow_methods[] = {
205-
{ "PRAMIN", load_vbios_pramin, true },
206-
{ "PROM", load_vbios_prom, false },
207-
{ "ACPI", load_vbios_acpi, true },
208-
{ "PCIROM", load_vbios_pci, true },
209-
};
210-
#define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods)
211-
212-
static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
213-
{
214-
struct methods *methods = shadow_methods;
215-
int testscore = 3;
216-
int scores[NUM_SHADOW_METHODS], i;
238+
static bool
239+
bios_shadow(struct drm_device *dev)
240+
{
241+
struct methods shadow_methods[] = {
242+
{ "PRAMIN", bios_shadow_pramin, true, 0, 0, NULL },
243+
{ "PROM", bios_shadow_prom, false, 0, 0, NULL },
244+
{ "ACPI", bios_shadow_acpi, true, 0, 0, NULL },
245+
{ "PCIROM", bios_shadow_pci, true, 0, 0, NULL },
246+
{}
247+
};
248+
struct drm_nouveau_private *dev_priv = dev->dev_private;
249+
struct nvbios *bios = &dev_priv->vbios;
250+
struct methods *mthd, *best;
217251

218252
if (nouveau_vbios) {
219-
for (i = 0; i < NUM_SHADOW_METHODS; i++)
220-
if (!strcasecmp(nouveau_vbios, methods[i].desc))
221-
break;
222-
223-
if (i < NUM_SHADOW_METHODS) {
224-
NV_INFO(dev, "Attempting to use BIOS image from %s\n",
225-
methods[i].desc);
253+
mthd = shadow_methods;
254+
do {
255+
if (strcasecmp(nouveau_vbios, mthd->desc))
256+
continue;
257+
NV_INFO(dev, "VBIOS source: %s\n", mthd->desc);
226258

227-
methods[i].loadbios(dev, data);
228-
if (score_vbios(dev, data, methods[i].rw))
259+
mthd->shadow(bios);
260+
mthd->score = score_vbios(bios, mthd->rw);
261+
if (mthd->score)
229262
return true;
230-
}
263+
} while ((++mthd)->shadow);
231264

232265
NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
233266
}
234267

235-
for (i = 0; i < NUM_SHADOW_METHODS; i++) {
236-
NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
237-
methods[i].desc);
238-
data[0] = data[1] = 0; /* avoid reuse of previous image */
239-
methods[i].loadbios(dev, data);
240-
scores[i] = score_vbios(dev, data, methods[i].rw);
241-
if (scores[i] == testscore)
242-
return true;
243-
}
244-
245-
while (--testscore > 0) {
246-
for (i = 0; i < NUM_SHADOW_METHODS; i++) {
247-
if (scores[i] == testscore) {
248-
NV_TRACE(dev, "Using BIOS image from %s\n",
249-
methods[i].desc);
250-
methods[i].loadbios(dev, data);
251-
return true;
252-
}
268+
mthd = shadow_methods;
269+
do {
270+
NV_TRACE(dev, "Checking %s for VBIOS\n", mthd->desc);
271+
mthd->shadow(bios);
272+
mthd->score = score_vbios(bios, mthd->rw);
273+
mthd->size = bios->length;
274+
mthd->data = bios->data;
275+
} while (mthd->score != 3 && (++mthd)->shadow);
276+
277+
mthd = shadow_methods;
278+
best = mthd;
279+
do {
280+
if (mthd->score > best->score) {
281+
kfree(best->data);
282+
best = mthd;
253283
}
284+
} while ((++mthd)->shadow);
285+
286+
if (best->score) {
287+
NV_TRACE(dev, "Using VBIOS from %s\n", best->desc);
288+
bios->length = best->size;
289+
bios->data = best->data;
290+
return true;
254291
}
255292

256-
NV_ERROR(dev, "No valid BIOS image found\n");
293+
NV_ERROR(dev, "No valid VBIOS image found\n");
257294
return false;
258295
}
259296

@@ -6334,11 +6371,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
63346371
spin_lock_init(&bios->lock);
63356372
bios->dev = dev;
63366373

6337-
if (!NVShadowVBIOS(dev, bios->data))
6338-
return false;
6339-
6340-
bios->length = NV_PROM_SIZE;
6341-
return true;
6374+
return bios_shadow(dev);
63426375
}
63436376

63446377
static int nouveau_parse_vbios_struct(struct drm_device *dev)
@@ -6498,6 +6531,10 @@ nouveau_bios_init(struct drm_device *dev)
64986531
void
64996532
nouveau_bios_takedown(struct drm_device *dev)
65006533
{
6534+
struct drm_nouveau_private *dev_priv = dev->dev_private;
6535+
65016536
nouveau_mxm_fini(dev);
65026537
nouveau_i2c_fini(dev);
6538+
6539+
kfree(dev_priv->vbios.data);
65036540
}

drivers/gpu/drm/nouveau/nouveau_bios.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ struct nvbios {
211211
NVBIOS_BIT
212212
} type;
213213
uint16_t offset;
214+
uint32_t length;
215+
uint8_t *data;
214216

215217
uint8_t chip_version;
216218

@@ -221,8 +223,6 @@ struct nvbios {
221223

222224
spinlock_t lock;
223225

224-
uint8_t data[NV_PROM_SIZE];
225-
unsigned int length;
226226
bool execute;
227227

228228
uint8_t major_version;

0 commit comments

Comments
 (0)