Nouveau源码分析(七)
虽然各个SUBDEV/EGINE的初始化实际还是在nouveau_drm_load里,但还是换个标题吧. 等把各个SUBDEV/ENGINE之类的说完再换回去.
上次已经按着初始化的顺序介绍了一下各个subdev的用途,现在按顺序,首先来看VBIOS的ctor函数:
-
- 537 struct nouveau_oclass
- 538 nouveau_bios_oclass = {
- 539 .handle = NV_SUBDEV(VBIOS, 0x00),
- 540 .ofuncs = &(struct nouveau_ofuncs) {
- 541 .ctor = nouveau_bios_ctor,
- 542 .dtor = nouveau_bios_dtor,
- 543 .init = nouveau_bios_init,
- 544 .fini = nouveau_bios_fini,
- 545 .rd08 = nouveau_bios_rd08,
- 546 .rd16 = nouveau_bios_rd16,
- 547 .rd32 = nouveau_bios_rd32,
- 548 .wr08 = nouveau_bios_wr08,
- 549 .wr16 = nouveau_bios_wr16,
- 550 .wr32 = nouveau_bios_wr32,
- 551 },
- 552 };
-
- 459 static int
- 460 nouveau_bios_ctor(struct nouveau_object *parent,
- 461 struct nouveau_object *engine,
- 462 struct nouveau_oclass *oclass, void *data, u32 size,
- 463 struct nouveau_object **pobject)
- 464 {
- 465 struct nouveau_bios *bios;
- 466 struct bit_entry bit_i;
- 467 int ret;
- 468
- 469 ret = nouveau_subdev_create(parent, engine, oclass, 0,
- 470 "VBIOS", "bios", &bios);
- 471 *pobject = nv_object(bios);
- 472 if (ret)
- 473 return ret;
- 474
- 475 ret = nouveau_bios_shadow(bios);
- 476 if (ret)
- 477 return ret;
- 478
- 479
- 480 bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
- 481 "\xff\x7f""NV\0", 5);
- 482 if (bios->bmp_offset) {
- 483 nv_info(bios, "BMP version %x.%x\n",
- 484 bmp_version(bios) >> 8,
- 485 bmp_version(bios) & 0xff);
- 486 }
- 487
- 488 bios->bit_offset = nvbios_findstr(bios->data, bios->size,
- 489 "\xff\xb8""BIT", 5);
- 490 if (bios->bit_offset)
- 491 nv_info(bios, "BIT signature found\n");
- 492
- 493
- 494 if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {
- 495 bios->version.major = nv_ro08(bios, bit_i.offset + 3);
- 496 bios->version.chip = nv_ro08(bios, bit_i.offset + 2);
- 497 bios->version.minor = nv_ro08(bios, bit_i.offset + 1);
- 498 bios->version.micro = nv_ro08(bios, bit_i.offset + 0);
- 499 bios->version.patch = nv_ro08(bios, bit_i.offset + 4);
- 500 } else
- 501 if (bmp_version(bios)) {
- 502 bios->version.major = nv_ro08(bios, bios->bmp_offset + 13);
- 503 bios->version.chip = nv_ro08(bios, bios->bmp_offset + 12);
- 504 bios->version.minor = nv_ro08(bios, bios->bmp_offset + 11);
- 505 bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10);
- 506 }
- 507
- 508 nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n",
- 509 bios->version.major, bios->version.chip,
- 510 bios->version.minor, bios->version.micro, bios->version.patch);
- 511
- 512 return 0;
- 513 }
第469行,首先创建一个subdev,这个函数我们已经分析过了.
第475行,寻找匹配BIOS:
-
- 328 static int
- 329 nouveau_bios_shadow(struct nouveau_bios *bios)
- 330 {
- 331 struct methods shadow_methods[] = {
- 332 #if defined(__powerpc__)
- 333 { "OpenFirmware", nouveau_bios_shadow_of, true, 0, 0, NULL },
- 334 #endif
- 335 { "PRAMIN", nouveau_bios_shadow_pramin, true, 0, 0, NULL },
- 336 { "PROM", nouveau_bios_shadow_prom, false, 0, 0, NULL },
- 337 { "ACPI", nouveau_bios_shadow_acpi, true, 0, 0, NULL },
- 338 { "PCIROM", nouveau_bios_shadow_pci, true, 0, 0, NULL },
- 339 { "PLATFORM", nouveau_bios_shadow_platform, true, 0, 0, NULL },
- 340 {}
- 341 };
- 342 struct methods *mthd, *best;
- 343 const struct firmware *fw;
- 344 const char *optarg;
- 345 int optlen, ret;
- 346 char *source;
- 347
- 348 optarg = nouveau_stropt(nv_device(bios)->cfgopt, "NvBios", &optlen);
- 349 source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
- 350 if (source) {
- 351
- 352 mthd = shadow_methods;
- 353 do {
- 354 if (strcasecmp(source, mthd->desc))
- 355 continue;
- 356 nv_info(bios, "source: %s\n", mthd->desc);
- 357
- 358 mthd->shadow(bios);
- 359 mthd->score = nouveau_bios_score(bios, mthd->rw);
- 360 if (mthd->score) {
- 361 kfree(source);
- 362 return 0;
- 363 }
- 364 } while ((++mthd)->shadow);
- 365
- 366
- 367 ret = request_firmware(&fw, source, &nv_device(bios)->pdev->dev);
- 368 if (ret == 0) {
- 369 bios->size = fw->size;
- 370 bios->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
- 371 release_firmware(fw);
- 372
- 373 nv_info(bios, "image: %s\n", source);
- 374 if (nouveau_bios_score(bios, 1)) {
- 375 kfree(source);
- 376 return 0;
- 377 }
- 378
- 379 kfree(bios->data);
- 380 bios->data = NULL;
- 381 }
- 382
- 383 nv_error(bios, "source \'%s\' invalid\n", source);
- 384 kfree(source);
- 385 }
- 386
- 387 mthd = shadow_methods;
- 388 do {
- 389 nv_info(bios, "checking %s for image...\n", mthd->desc);
- 390 mthd->shadow(bios);
- 391 mthd->score = nouveau_bios_score(bios, mthd->rw);
- 392 mthd->size = bios->size;
- 393 mthd->data = bios->data;
- 394 bios->data = NULL;
- 395 } while (mthd->score != 3 && (++mthd)->shadow);
- 396
- 397 mthd = shadow_methods;
- 398 best = mthd;
- 399 do {
- 400 if (mthd->score > best->score) {
- 401 kfree(best->data);
- 402 best = mthd;
- 403 }
- 404 } while ((++mthd)->shadow);
- 405
- 406 if (best->score) {
- 407 nv_info(bios, "using image from %s\n", best->desc);
- 408 bios->size = best->size;
- 409 bios->data = best->data;
- 410 return 0;
- 411 }
- 412
- 413 nv_error(bios, "unable to locate usable image\n");
- 414 return -EINVAL;
- 415 }
首先,第331行,列出BIOS的几种来源,识别函数,是否可写等信息.
第348行,从cfgopt中检查是否加载模块时特别指定了所要的BIOS类型,一般来说不指定的话,第350行BIOS语句直接跳过.
第387行,准备识别BIOS.
然后第389行的while语句,首先调用shadow函数指针来检查获取这种类型的BIOS.
再使用nouveau_bios_score函数,得到这个BIOS的"成绩". 简单看一下这个函数:
-
- 299 static int
- 300 nouveau_bios_score(struct nouveau_bios *bios, const bool writeable)
- 301 {
- 302 if (bios->size < 3 || !bios->data || bios->data[0] != 0x55 ||
- 303 bios->data[1] != 0xAA) {
- 304 nv_info(bios, "... signature not found\n");
- 305 return 0;
- 306 }
- 307
- 308 if (nvbios_checksum(bios->data,
- 309 min_t(u32, bios->data[2] * 512, bios->size))) {
- 310 nv_info(bios, "... checksum invalid\n");
- 311
- 312 return writeable ? 2 : 1;
- 313 }
- 314
- 315 nv_info(bios, "... appears to be valid\n");
- 316 return 3;
- 317 }
首先检查魔数是否匹配,不匹配直接返回0.
然后checksum,如果匹配返回3. 不匹配的话,BIOS可写2分,不可写1分. [因为可写的可能被污染了.]
回到nouveau_bios_ctor函数,如果得到的分数是3,也就是最高分,那么退出while循环,不然继续下次循环.
第399行,后面紧跟着还是一个while循环,它的作用就是挑选出得分最高的那个BIOS来使用.
第408行,把分数最高的BIOS放进结构体中并返回.
第414行,如果所有BIOS得分都是0分,那么返回-EINVAL.
对于那几种BIOS的识别函数,我们挑选一个来看:
-
- 84 static void
- 85 nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
- 86 {
- 87 struct nouveau_device *device = nv_device(bios);
- 88 u64 addr = 0;
- 89 u32 bar0 = 0;
- 90 int i;
- 91
- 92 if (device->card_type >= NV_50) {
- 93 if (device->card_type >= NV_C0 && device->card_type < GM100) {
- 94 if (nv_rd32(bios, 0x022500) & 0x00000001)
- 95 return;
- 96 } else
- 97 if (device->card_type >= GM100) {
- 98 if (nv_rd32(bios, 0x021c04) & 0x00000001)
- 99 return;
- 100 }
- 101
- 102 addr = nv_rd32(bios, 0x619f04);
- 103 if (!(addr & 0x00000008)) {
- 104 nv_debug(bios, "... not enabled\n");
- 105 return;
- 106 }
- 107 if ( (addr & 0x00000003) != 1) {
- 108 nv_debug(bios, "... not in vram\n");
- 109 return;
- 110 }
- 111
- 112 addr = (addr & 0xffffff00) << 8;
- 113 if (!addr) {
- 114 addr = (u64)nv_rd32(bios, 0x001700) << 16;
- 115 addr += 0xf0000;
- 116 }
- 117
- 118 bar0 = nv_mask(bios, 0x001700, 0xffffffff, addr >> 16);
- 119 }
- 120
- 121
- 122 if (nv_rd08(bios, 0x700000) != 0x55 ||
- 123 nv_rd08(bios, 0x700001) != 0xaa)
- 124 goto out;
- 125
- 126 bios->size = nv_rd08(bios, 0x700002) * 512;
- 127 if (!bios->size)
- 128 goto out;
- 129
- 130 bios->data = kmalloc(bios->size, GFP_KERNEL);
- 131 if (bios->data) {
- 132 for (i = 0; i < bios->size; i++)
- 133 nv_wo08(bios, i, nv_rd08(bios, 0x700000 + i));
- 134 }
- 135
- 136 out:
- 137 if (device->card_type >= NV_50)
- 138 nv_wr32(bios, 0x001700, bar0);
- 139 }
首先,这里先来看几个读写寄存器或其他东西的函数: nv_wo32 nv_wr32 [以32位的读为例,其他的类比很容易猜到] .
-
- 102 static inline void
- 103 nv_wr32(void *obj, u32 addr, u32 data)
- 104 {
- 105 struct nouveau_subdev *subdev = nv_subdev(obj);
- 106 nv_spam(subdev, "nv_wr32 0x%06x 0x%08x\n", addr, data);
- 107 iowrite32_native(data, subdev->mmio + addr);
- 108 }
一看到iowrite32_native,很明显是对MMIO空间寄存器的操作. 这个要求obj必须是一个subdev. 回想一下subdev的mmio字段在哪见过? 我们遇见过两次,一次是在nouveau_subdev_create创建subdev的时候,把新创建的subdev的mmio字段的值赋上了parent的mmio字段的值. 那么再往上,parent的mmio字段的值又是从哪来的呢? 这就是另一次了,在nouveau_devobj_create中,把device的mmio字段初始化为PCI设备中BAR0的地址.
那么很明显了,不管对什么obj进行这个nv_wr32,一定是向PCI设备BAR0地址指示的MMIO地址写. [只讨论PCI总线,其他另说.]
-
- 177 static inline void
- 178 nv_wo32(void *obj, u64 addr, u32 data)
- 179 {
- 180 nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data);
- 181 nv_ofuncs(obj)->wr32(obj, addr, data);
- 182 }
这个是调用的obj的oclass里指示的函数指针wr32. 因此这个函数的作用可就是"因obj而异"了.例如,对于BIOS,这是向BIOS数据里写;对于instmem或gpuobj,这是向显存里写.
现在我们讨论BIOS当然就要看看BIOS的wr32函数指针:
-
- 452 static void
- 453 nouveau_bios_wr32(struct nouveau_object *object, u64 addr, u32 data)
- 454 {
- 455 struct nouveau_bios *bios = (void *)object;
- 456 put_unaligned_le32(data, &bios->data[addr]);
- 457 }
bios->data就是从BIOS数据区里复制出来的BIOS数据,稍等一会我们就能看到它的初始化. 这个函数当然就是对其中的数据进行写.
回到nouveau_bios_shadow_pramin,第94行,上下文推断就是检测这种BIOS是否存在,不存在就返回.
第103行,通过看调试信息知道作用是判断这种BIOS是否启用了.
第107行,判断BIOS是否在显存内.
第112行,进行适当的mask,得到BIOS在显存中的地址. 当然有时候这个地址是无效的,那么在下面的if语句中,我们将使用默认的地址.
这里有一个寄存器出现了多次,就是0x1700,等会要讲的第118行还对其进行了mask操作,那么这个寄存器这个的作用是什么呢? 可以结合envytools的一段代码理解:
-
-
-
- uint32_t vram_rd32(int card, uint64_t addr) {
- if (nva_cards[card]->chipset.card_type < 3) {
- return nva_rd32(card, 0x1000000 + addr);
- } else if (nva_cards[card]->chipset.card_type < 0x30) {
- return nva_grd32(nva_cards[card]->bar1, addr);
- } else if (nva_cards[card]->chipset.card_type < 0x50) {
- nva_wr32(card, 0x1570, addr);
- return nva_rd32(card, 0x1574);
- } else {
- uint32_t old = nva_rd32(card, 0x1700);
- nva_wr32(card, 0x1700, addr >> 16);
- uint32_t res = nva_rd32(card, 0x700000 | (addr & 0xffff));
- nva_wr32(card, 0x1700, old);
- return res;
- }
- }
函数名很明显是读取显存,刚刚我们获取这BIOS地址也是在显存里的,我们要想办法把他复制出来.
那么PCI设备的BAR不是已经给映射好了吗,为什么不用呢? 因为BAR映射的都是虚拟地址,现在我们完全还无法知道虚拟地址和物理地址的对应关系,这部分要到VM才被初始化. 所以我们只能想其他办法,来看看这个函数的实现吧. 首先判断显卡类型,这个我们只看NV50以后的.
首先把0x1700寄存器读出来存到old里,然后把addr >> 16 写进去,接着从0x700000 | (addr & 0xffff)中读出返回值,再把0x1700复原.
应该很容易理解这个0x1700和0x700000的作用了,0x1700决定0x700000 ~ 0x70ffff映射的物理地址,然后我们就能从中读取数据了.
那么回到第118行,再来看一下nv_mask吧:
-
- 110 static inline u32
- 111 nv_mask(void *obj, u32 addr, u32 mask, u32 data)
- 112 {
- 113 u32 temp = nv_rd32(obj, addr);
- 114 nv_wr32(obj, addr, (temp & ~mask) | data);
- 115 return temp;
- 116 }
- 117
先读寄存器,然后mask掉参数mask,or上参数data. 对于这个例子,mask就是0xffffffff,所以真正写进去的东西就是这个data,也就是刚刚的addr >> 16.
于是,这样的话0x700000就映射上了地址addr指示的BIOS数据,当务之急就是把它取出来.
在此之前,第122行,校验魔数. 第126行,获取BIOS的大小.
如果这两步都没出错,那么第130行,分配一个等同BIOS大小的数据区. 第133行,把BIOS中的数据复制出来.
最后,第138行,恢复0x1700寄存器的值. 返回.
当然BIOS的储存位置不止着一个,还可能储存在其他地方,但这些处理函数都大同小异,无非就是读取拷贝出来,感兴趣可以自己去看源代码.
返回ctor函数,接下来的任务就是检测BIOS的版本:
第480行,虽然我们在这里发现了一个新函数,但我们并不需要去看它的内容——只看函数名足够了. 在BIOS中寻找特定字符串,这个东西叫做BMP.
第488行,寻找另一个字符串,这个叫做BIT.
第494行,这个if语句就是首先在bit_offset中寻找BIOS的版本信息,找到就储存起来.
-
- 30 int
- 31 bit_entry(struct nouveau_bios *bios, u8 id, struct bit_entry *bit)
- 32 {
- 33 if (likely(bios->bit_offset)) {
- 34 u8 entries = nv_ro08(bios, bios->bit_offset + 10);
- 35 u32 entry = bios->bit_offset + 12;
- 36 while (entries--) {
- 37 if (nv_ro08(bios, entry + 0) == id) {
- 38 bit->id = nv_ro08(bios, entry + 0);
- 39 bit->version = nv_ro08(bios, entry + 1);
- 40 bit->length = nv_ro16(bios, entry + 2);
- 41 bit->offset = nv_ro16(bios, entry + 4);
- 42 return 0;
- 43 }
- 44
- 45 entry += nv_ro08(bios, bios->bit_offset + 9);
- 46 }
- 47
- 48 return -ENOENT;
- 49 }
- 50
- 51 return -EINVAL;
- 52 }
第34行,先得到起始地址entry和个数entries.
第37行,遍历每一个entry,并且判断是否符合传入的id,然后返回之.
第501行,另一个if语句,在bit寻找失败的情况下判断bmp是否可用,可用就储存版本号.
第508行,输出调试信息,返回.
至此我们成功创建了第一个subdev,但我们并不会立即去执行它的init函数,在此之前,我们还要先执行devinit,i2c,gpio的ctor函数.
版权声明:本文为博主原创文章,未经博主允许不得转载。