xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/base.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
24d390b480SBen Skeggs #include "priv.h"
25c39f472eSBen Skeggs 
26c39f472eSBen Skeggs #include <subdev/bios.h>
27c39f472eSBen Skeggs #include <subdev/bios/bmp.h>
28c39f472eSBen Skeggs #include <subdev/bios/bit.h>
292f96e8e3SBen Skeggs #include <subdev/bios/image.h>
30c39f472eSBen Skeggs 
314d4e9907SBen Skeggs static bool
nvbios_addr(struct nvkm_bios * bios,u32 * addr,u8 size)324d4e9907SBen Skeggs nvbios_addr(struct nvkm_bios *bios, u32 *addr, u8 size)
334d4e9907SBen Skeggs {
342f96e8e3SBen Skeggs 	u32 p = *addr;
352f96e8e3SBen Skeggs 
36*c441d289STimur Tabi 	if (*addr >= bios->image0_size && bios->imaged_addr) {
372f96e8e3SBen Skeggs 		*addr -= bios->image0_size;
382f96e8e3SBen Skeggs 		*addr += bios->imaged_addr;
392f96e8e3SBen Skeggs 	}
402f96e8e3SBen Skeggs 
411b777d4dSNick Lopez 	if (unlikely(*addr + size > bios->size)) {
422f96e8e3SBen Skeggs 		nvkm_error(&bios->subdev, "OOB %d %08x %08x\n", size, p, *addr);
434d4e9907SBen Skeggs 		return false;
444d4e9907SBen Skeggs 	}
452f96e8e3SBen Skeggs 
464d4e9907SBen Skeggs 	return true;
474d4e9907SBen Skeggs }
484d4e9907SBen Skeggs 
494d4e9907SBen Skeggs u8
nvbios_rd08(struct nvkm_bios * bios,u32 addr)504d4e9907SBen Skeggs nvbios_rd08(struct nvkm_bios *bios, u32 addr)
514d4e9907SBen Skeggs {
524d4e9907SBen Skeggs 	if (likely(nvbios_addr(bios, &addr, 1)))
534d4e9907SBen Skeggs 		return bios->data[addr];
544d4e9907SBen Skeggs 	return 0x00;
554d4e9907SBen Skeggs }
564d4e9907SBen Skeggs 
574d4e9907SBen Skeggs u16
nvbios_rd16(struct nvkm_bios * bios,u32 addr)584d4e9907SBen Skeggs nvbios_rd16(struct nvkm_bios *bios, u32 addr)
594d4e9907SBen Skeggs {
604d4e9907SBen Skeggs 	if (likely(nvbios_addr(bios, &addr, 2)))
614d4e9907SBen Skeggs 		return get_unaligned_le16(&bios->data[addr]);
624d4e9907SBen Skeggs 	return 0x0000;
634d4e9907SBen Skeggs }
644d4e9907SBen Skeggs 
654d4e9907SBen Skeggs u32
nvbios_rd32(struct nvkm_bios * bios,u32 addr)664d4e9907SBen Skeggs nvbios_rd32(struct nvkm_bios *bios, u32 addr)
674d4e9907SBen Skeggs {
684d4e9907SBen Skeggs 	if (likely(nvbios_addr(bios, &addr, 4)))
694d4e9907SBen Skeggs 		return get_unaligned_le32(&bios->data[addr]);
704d4e9907SBen Skeggs 	return 0x00000000;
714d4e9907SBen Skeggs }
724d4e9907SBen Skeggs 
73c39f472eSBen Skeggs u8
nvbios_checksum(const u8 * data,int size)74c39f472eSBen Skeggs nvbios_checksum(const u8 *data, int size)
75c39f472eSBen Skeggs {
76c39f472eSBen Skeggs 	u8 sum = 0;
77c39f472eSBen Skeggs 	while (size--)
78c39f472eSBen Skeggs 		sum += *data++;
79c39f472eSBen Skeggs 	return sum;
80c39f472eSBen Skeggs }
81c39f472eSBen Skeggs 
82c39f472eSBen Skeggs u16
nvbios_findstr(const u8 * data,int size,const char * str,int len)83c39f472eSBen Skeggs nvbios_findstr(const u8 *data, int size, const char *str, int len)
84c39f472eSBen Skeggs {
85c39f472eSBen Skeggs 	int i, j;
86c39f472eSBen Skeggs 
87c39f472eSBen Skeggs 	for (i = 0; i <= (size - len); i++) {
88c39f472eSBen Skeggs 		for (j = 0; j < len; j++)
89c39f472eSBen Skeggs 			if ((char)data[i + j] != str[j])
90c39f472eSBen Skeggs 				break;
91c39f472eSBen Skeggs 		if (j == len)
92c39f472eSBen Skeggs 			return i;
93c39f472eSBen Skeggs 	}
94c39f472eSBen Skeggs 
95c39f472eSBen Skeggs 	return 0;
96c39f472eSBen Skeggs }
97c39f472eSBen Skeggs 
98c39f472eSBen Skeggs int
nvbios_memcmp(struct nvkm_bios * bios,u32 addr,const char * str,u32 len)997f5f518fSBen Skeggs nvbios_memcmp(struct nvkm_bios *bios, u32 addr, const char *str, u32 len)
1007f5f518fSBen Skeggs {
1017f5f518fSBen Skeggs 	unsigned char c1, c2;
1027f5f518fSBen Skeggs 
1037f5f518fSBen Skeggs 	while (len--) {
1047f5f518fSBen Skeggs 		c1 = nvbios_rd08(bios, addr++);
1057f5f518fSBen Skeggs 		c2 = *(str++);
1067f5f518fSBen Skeggs 		if (c1 != c2)
1077f5f518fSBen Skeggs 			return c1 - c2;
1087f5f518fSBen Skeggs 	}
1097f5f518fSBen Skeggs 	return 0;
1107f5f518fSBen Skeggs }
1117f5f518fSBen Skeggs 
1127f5f518fSBen Skeggs int
nvbios_extend(struct nvkm_bios * bios,u32 length)113d390b480SBen Skeggs nvbios_extend(struct nvkm_bios *bios, u32 length)
114c39f472eSBen Skeggs {
115c39f472eSBen Skeggs 	if (bios->size < length) {
116c39f472eSBen Skeggs 		u8 *prev = bios->data;
117c39f472eSBen Skeggs 		if (!(bios->data = kmalloc(length, GFP_KERNEL))) {
118c39f472eSBen Skeggs 			bios->data = prev;
119c39f472eSBen Skeggs 			return -ENOMEM;
120c39f472eSBen Skeggs 		}
121c39f472eSBen Skeggs 		memcpy(bios->data, prev, bios->size);
122c39f472eSBen Skeggs 		bios->size = length;
123c39f472eSBen Skeggs 		kfree(prev);
124c39f472eSBen Skeggs 		return 1;
125c39f472eSBen Skeggs 	}
126c39f472eSBen Skeggs 	return 0;
127c39f472eSBen Skeggs }
128c39f472eSBen Skeggs 
12946484438SBen Skeggs static void *
nvkm_bios_dtor(struct nvkm_subdev * subdev)13046484438SBen Skeggs nvkm_bios_dtor(struct nvkm_subdev *subdev)
13146484438SBen Skeggs {
13246484438SBen Skeggs 	struct nvkm_bios *bios = nvkm_bios(subdev);
13346484438SBen Skeggs 	kfree(bios->data);
13446484438SBen Skeggs 	return bios;
13546484438SBen Skeggs }
13646484438SBen Skeggs 
13746484438SBen Skeggs static const struct nvkm_subdev_func
13846484438SBen Skeggs nvkm_bios = {
13946484438SBen Skeggs 	.dtor = nvkm_bios_dtor,
14046484438SBen Skeggs };
14146484438SBen Skeggs 
14246484438SBen Skeggs int
nvkm_bios_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_bios ** pbios)143e07f50d3SBen Skeggs nvkm_bios_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
144e07f50d3SBen Skeggs 	      struct nvkm_bios **pbios)
145c39f472eSBen Skeggs {
146d390b480SBen Skeggs 	struct nvkm_bios *bios;
1472f96e8e3SBen Skeggs 	struct nvbios_image image;
148c39f472eSBen Skeggs 	struct bit_entry bit_i;
1492f96e8e3SBen Skeggs 	int ret, idx = 0;
150c39f472eSBen Skeggs 
15146484438SBen Skeggs 	if (!(bios = *pbios = kzalloc(sizeof(*bios), GFP_KERNEL)))
15246484438SBen Skeggs 		return -ENOMEM;
153e07f50d3SBen Skeggs 	nvkm_subdev_ctor(&nvkm_bios, device, type, inst, &bios->subdev);
154c39f472eSBen Skeggs 
155c39f472eSBen Skeggs 	ret = nvbios_shadow(bios);
156c39f472eSBen Skeggs 	if (ret)
157c39f472eSBen Skeggs 		return ret;
158c39f472eSBen Skeggs 
1592f96e8e3SBen Skeggs 	/* Some tables have weird pointers that need adjustment before
1602f96e8e3SBen Skeggs 	 * they're dereferenced.  I'm not entirely sure why...
1612f96e8e3SBen Skeggs 	 */
1622f96e8e3SBen Skeggs 	if (nvbios_image(bios, idx++, &image)) {
1632f96e8e3SBen Skeggs 		bios->image0_size = image.size;
1642f96e8e3SBen Skeggs 		while (nvbios_image(bios, idx++, &image)) {
1652f96e8e3SBen Skeggs 			if (image.type == 0xe0) {
1662f96e8e3SBen Skeggs 				bios->imaged_addr = image.base;
1672f96e8e3SBen Skeggs 				break;
1682f96e8e3SBen Skeggs 			}
1692f96e8e3SBen Skeggs 		}
1702f96e8e3SBen Skeggs 	}
1712f96e8e3SBen Skeggs 
172c39f472eSBen Skeggs 	/* detect type of vbios we're dealing with */
173c39f472eSBen Skeggs 	bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
174c39f472eSBen Skeggs 					  "\xff\x7f""NV\0", 5);
175c39f472eSBen Skeggs 	if (bios->bmp_offset) {
17660b29d20SBen Skeggs 		nvkm_debug(&bios->subdev, "BMP version %x.%x\n",
177c39f472eSBen Skeggs 			   bmp_version(bios) >> 8,
178c39f472eSBen Skeggs 			   bmp_version(bios) & 0xff);
179c39f472eSBen Skeggs 	}
180c39f472eSBen Skeggs 
181c39f472eSBen Skeggs 	bios->bit_offset = nvbios_findstr(bios->data, bios->size,
182c39f472eSBen Skeggs 					  "\xff\xb8""BIT", 5);
183c39f472eSBen Skeggs 	if (bios->bit_offset)
18460b29d20SBen Skeggs 		nvkm_debug(&bios->subdev, "BIT signature found\n");
185c39f472eSBen Skeggs 
186c39f472eSBen Skeggs 	/* determine the vbios version number */
187c39f472eSBen Skeggs 	if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {
1887f5f518fSBen Skeggs 		bios->version.major = nvbios_rd08(bios, bit_i.offset + 3);
1897f5f518fSBen Skeggs 		bios->version.chip  = nvbios_rd08(bios, bit_i.offset + 2);
1907f5f518fSBen Skeggs 		bios->version.minor = nvbios_rd08(bios, bit_i.offset + 1);
1917f5f518fSBen Skeggs 		bios->version.micro = nvbios_rd08(bios, bit_i.offset + 0);
1927f5f518fSBen Skeggs 		bios->version.patch = nvbios_rd08(bios, bit_i.offset + 4);
193c39f472eSBen Skeggs 	} else
194c39f472eSBen Skeggs 	if (bmp_version(bios)) {
1957f5f518fSBen Skeggs 		bios->version.major = nvbios_rd08(bios, bios->bmp_offset + 13);
1967f5f518fSBen Skeggs 		bios->version.chip  = nvbios_rd08(bios, bios->bmp_offset + 12);
1977f5f518fSBen Skeggs 		bios->version.minor = nvbios_rd08(bios, bios->bmp_offset + 11);
1987f5f518fSBen Skeggs 		bios->version.micro = nvbios_rd08(bios, bios->bmp_offset + 10);
199c39f472eSBen Skeggs 	}
200c39f472eSBen Skeggs 
20160b29d20SBen Skeggs 	nvkm_info(&bios->subdev, "version %02x.%02x.%02x.%02x.%02x\n",
202c39f472eSBen Skeggs 		  bios->version.major, bios->version.chip,
203c39f472eSBen Skeggs 		  bios->version.minor, bios->version.micro, bios->version.patch);
204c39f472eSBen Skeggs 	return 0;
205c39f472eSBen Skeggs }
206