Nouveau源码分析(七): 各SUBDEV/ENGINE初始化 (1)

Nouveau源码分析(七)

虽然各个SUBDEV/EGINE的初始化实际还是在nouveau_drm_load里,但还是换个标题吧. 等把各个SUBDEV/ENGINE之类的说完再换回去.

上次已经按着初始化的顺序介绍了一下各个subdev的用途,现在按顺序,首先来看VBIOS的ctor函数:

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2. 537 struct nouveau_oclass  
  3. 538 nouveau_bios_oclass = {  
  4. 539         .handle = NV_SUBDEV(VBIOS, 0x00),  
  5. 540         .ofuncs = &(struct nouveau_ofuncs) {  
  6. 541                 .ctor = nouveau_bios_ctor,  
  7. 542                 .dtor = nouveau_bios_dtor,  
  8. 543                 .init = nouveau_bios_init,  
  9. 544                 .fini = nouveau_bios_fini,  
  10. 545                 .rd08 = nouveau_bios_rd08,  
  11. 546                 .rd16 = nouveau_bios_rd16,  
  12. 547                 .rd32 = nouveau_bios_rd32,  
  13. 548                 .wr08 = nouveau_bios_wr08,  
  14. 549                 .wr16 = nouveau_bios_wr16,  
  15. 550                 .wr32 = nouveau_bios_wr32,  
  16. 551         },  
  17. 552 };  

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2. 459 static int  
  3. 460 nouveau_bios_ctor(struct nouveau_object *parent,  
  4. 461                   struct nouveau_object *engine,  
  5. 462                   struct nouveau_oclass *oclass, void *data, u32 size,  
  6. 463                   struct nouveau_object **pobject)  
  7. 464 {  
  8. 465         struct nouveau_bios *bios;  
  9. 466         struct bit_entry bit_i;  
  10. 467         int ret;  
  11. 468   
  12. 469         ret = nouveau_subdev_create(parent, engine, oclass, 0,  
  13. 470                                     "VBIOS""bios", &bios);  
  14. 471         *pobject = nv_object(bios);  
  15. 472         if (ret)  
  16. 473                 return ret;  
  17. 474   
  18. 475         ret = nouveau_bios_shadow(bios);  
  19. 476         if (ret)  
  20. 477                 return ret;  
  21. 478   
  22. 479         /* detect type of vbios we're dealing with */  
  23. 480         bios->bmp_offset = nvbios_findstr(bios->data, bios->size,  
  24. 481                                           "\xff\x7f""NV\0", 5);  
  25. 482         if (bios->bmp_offset) {  
  26. 483                 nv_info(bios, "BMP version %x.%x\n",  
  27. 484                         bmp_version(bios) >> 8,  
  28. 485                         bmp_version(bios) & 0xff);  
  29. 486         }  
  30. 487   
  31. 488         bios->bit_offset = nvbios_findstr(bios->data, bios->size,  
  32. 489                                           "\xff\xb8""BIT", 5);  
  33. 490         if (bios->bit_offset)  
  34. 491                 nv_info(bios, "BIT signature found\n");  
  35. 492   
  36. 493         /* determine the vbios version number */  
  37. 494         if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {  
  38. 495                 bios->version.major = nv_ro08(bios, bit_i.offset + 3);  
  39. 496                 bios->version.chip  = nv_ro08(bios, bit_i.offset + 2);  
  40. 497                 bios->version.minor = nv_ro08(bios, bit_i.offset + 1);  
  41. 498                 bios->version.micro = nv_ro08(bios, bit_i.offset + 0);  
  42. 499                 bios->version.patch = nv_ro08(bios, bit_i.offset + 4);  
  43. 500         } else  
  44. 501         if (bmp_version(bios)) {  
  45. 502                 bios->version.major = nv_ro08(bios, bios->bmp_offset + 13);  
  46. 503                 bios->version.chip  = nv_ro08(bios, bios->bmp_offset + 12);  
  47. 504                 bios->version.minor = nv_ro08(bios, bios->bmp_offset + 11);  
  48. 505                 bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10);  
  49. 506         }  
  50. 507   
  51. 508         nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n",  
  52. 509                 bios->version.major, bios->version.chip,  
  53. 510                 bios->version.minor, bios->version.micro, bios->version.patch);  
  54. 511   
  55. 512         return 0;  
  56. 513 }  
第469行,首先创建一个subdev,这个函数我们已经分析过了.

第475行,寻找匹配BIOS:

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2. 328 static int  
  3. 329 nouveau_bios_shadow(struct nouveau_bios *bios)  
  4. 330 {  
  5. 331         struct methods shadow_methods[] = {  
  6. 332 #if defined(__powerpc__)  
  7. 333                 { "OpenFirmware", nouveau_bios_shadow_of, true, 0, 0, NULL },  
  8. 334 #endif  
  9. 335                 { "PRAMIN", nouveau_bios_shadow_pramin, true, 0, 0, NULL },  
  10. 336                 { "PROM", nouveau_bios_shadow_prom, false, 0, 0, NULL },  
  11. 337                 { "ACPI", nouveau_bios_shadow_acpi, true, 0, 0, NULL },  
  12. 338                 { "PCIROM", nouveau_bios_shadow_pci, true, 0, 0, NULL },  
  13. 339                 { "PLATFORM", nouveau_bios_shadow_platform, true, 0, 0, NULL },  
  14. 340                 {}  
  15. 341         };  
  16. 342         struct methods *mthd, *best;  
  17. 343         const struct firmware *fw;  
  18. 344         const char *optarg;  
  19. 345         int optlen, ret;  
  20. 346         char *source;  
  21. 347   
  22. 348         optarg = nouveau_stropt(nv_device(bios)->cfgopt, "NvBios", &optlen);  
  23. 349         source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;  
  24. 350         if (source) {  
  25. 351                 /* try to match one of the built-in methods */  
  26. 352                 mthd = shadow_methods;  
  27. 353                 do {  
  28. 354                         if (strcasecmp(source, mthd->desc))  
  29. 355                                 continue;  
  30. 356                         nv_info(bios, "source: %s\n", mthd->desc);  
  31. 357   
  32. 358                         mthd->shadow(bios);  
  33. 359                         mthd->score = nouveau_bios_score(bios, mthd->rw);  
  34. 360                         if (mthd->score) {  
  35. 361                                 kfree(source);  
  36. 362                                 return 0;  
  37. 363                         }  
  38. 364                 } while ((++mthd)->shadow);  
  39. 365   
  40. 366                 /* attempt to load firmware image */  
  41. 367                 ret = request_firmware(&fw, source, &nv_device(bios)->pdev->dev);  
  42. 368                 if (ret == 0) {  
  43. 369                         bios->size = fw->size;  
  44. 370                         bios->data = kmemdup(fw->data, fw->size, GFP_KERNEL);  
  45. 371                         release_firmware(fw);  
  46. 372   
  47. 373                         nv_info(bios, "image: %s\n", source);  
  48. 374                         if (nouveau_bios_score(bios, 1)) {  
  49. 375                                 kfree(source);  
  50. 376                                 return 0;  
  51. 377                         }  
  52. 378   
  53. 379                         kfree(bios->data);  
  54. 380                         bios->data = NULL;  
  55. 381                 }  
  56. 382   
  57. 383                 nv_error(bios, "source \'%s\' invalid\n", source);  
  58. 384                 kfree(source);  
  59. 385         }  
  60. 386   
  61. 387         mthd = shadow_methods;  
  62. 388         do {  
  63. 389                 nv_info(bios, "checking %s for image...\n", mthd->desc);  
  64. 390                 mthd->shadow(bios);  
  65. 391                 mthd->score = nouveau_bios_score(bios, mthd->rw);  
  66. 392                 mthd->size = bios->size;  
  67. 393                 mthd->data = bios->data;  
  68. 394                 bios->data = NULL;  
  69. 395         } while (mthd->score != 3 && (++mthd)->shadow);  
  70. 396   
  71. 397         mthd = shadow_methods;  
  72. 398         best = mthd;  
  73. 399         do {  
  74. 400                 if (mthd->score > best->score) {  
  75. 401                         kfree(best->data);  
  76. 402                         best = mthd;  
  77. 403                 }  
  78. 404         } while ((++mthd)->shadow);  
  79. 405   
  80. 406         if (best->score) {  
  81. 407                 nv_info(bios, "using image from %s\n", best->desc);  
  82. 408                 bios->size = best->size;  
  83. 409                 bios->data = best->data;  
  84. 410                 return 0;  
  85. 411         }  
  86. 412   
  87. 413         nv_error(bios, "unable to locate usable image\n");  
  88. 414         return -EINVAL;  
  89. 415 }  
首先,第331行,列出BIOS的几种来源,识别函数,是否可写等信息.

第348行,从cfgopt中检查是否加载模块时特别指定了所要的BIOS类型,一般来说不指定的话,第350行BIOS语句直接跳过.

第387行,准备识别BIOS.

然后第389行的while语句,首先调用shadow函数指针来检查获取这种类型的BIOS.

再使用nouveau_bios_score函数,得到这个BIOS的"成绩". 简单看一下这个函数:

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2. 299 static int  
  3. 300 nouveau_bios_score(struct nouveau_bios *bios, const bool writeable)  
  4. 301 {  
  5. 302         if (bios->size < 3 || !bios->data || bios->data[0] != 0x55 ||  
  6. 303                         bios->data[1] != 0xAA) {  
  7. 304                 nv_info(bios, "... signature not found\n");  
  8. 305                 return 0;  
  9. 306         }  
  10. 307   
  11. 308         if (nvbios_checksum(bios->data,  
  12. 309                         min_t(u32, bios->data[2] * 512, bios->size))) {  
  13. 310                 nv_info(bios, "... checksum invalid\n");  
  14. 311                 /* if a ro image is somewhat bad, it's probably all rubbish */  
  15. 312                 return writeable ? 2 : 1;  
  16. 313         }  
  17. 314   
  18. 315         nv_info(bios, "... appears to be valid\n");  
  19. 316         return 3;  
  20. 317 }  
首先检查魔数是否匹配,不匹配直接返回0.

然后checksum,如果匹配返回3. 不匹配的话,BIOS可写2分,不可写1分. [因为可写的可能被污染了.]


回到nouveau_bios_ctor函数,如果得到的分数是3,也就是最高分,那么退出while循环,不然继续下次循环.

第399行,后面紧跟着还是一个while循环,它的作用就是挑选出得分最高的那个BIOS来使用.

第408行,把分数最高的BIOS放进结构体中并返回.

第414行,如果所有BIOS得分都是0分,那么返回-EINVAL.


对于那几种BIOS的识别函数,我们挑选一个来看:

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2.  84 static void  
  3.  85 nouveau_bios_shadow_pramin(struct nouveau_bios *bios)  
  4.  86 {  
  5.  87         struct nouveau_device *device = nv_device(bios);  
  6.  88         u64 addr = 0;  
  7.  89         u32 bar0 = 0;  
  8.  90         int i;  
  9.  91   
  10.  92         if (device->card_type >= NV_50) {  
  11.  93                 if (device->card_type >= NV_C0 && device->card_type < GM100) {  
  12.  94                         if (nv_rd32(bios, 0x022500) & 0x00000001)  
  13.  95                                 return;  
  14.  96                 } else  
  15.  97                 if (device->card_type >= GM100) {  
  16.  98                         if (nv_rd32(bios, 0x021c04) & 0x00000001)  
  17.  99                                 return;  
  18. 100                 }  
  19. 101   
  20. 102                 addr = nv_rd32(bios, 0x619f04);  
  21. 103                 if (!(addr & 0x00000008)) {  
  22. 104                         nv_debug(bios, "... not enabled\n");  
  23. 105                         return;  
  24. 106                 }  
  25. 107                 if ( (addr & 0x00000003) != 1) {  
  26. 108                         nv_debug(bios, "... not in vram\n");  
  27. 109                         return;  
  28. 110                 }  
  29. 111   
  30. 112                 addr = (addr & 0xffffff00) << 8;  
  31. 113                 if (!addr) {  
  32. 114                         addr  = (u64)nv_rd32(bios, 0x001700) << 16;  
  33. 115                         addr += 0xf0000;  
  34. 116                 }  
  35. 117   
  36. 118                 bar0 = nv_mask(bios, 0x001700, 0xffffffff, addr >> 16);  
  37. 119         }  
  38. 120   
  39. 121         /* bail if no rom signature */  
  40. 122         if (nv_rd08(bios, 0x700000) != 0x55 ||  
  41. 123             nv_rd08(bios, 0x700001) != 0xaa)  
  42. 124                 goto out;  
  43. 125   
  44. 126         bios->size = nv_rd08(bios, 0x700002) * 512;  
  45. 127         if (!bios->size)  
  46. 128                 goto out;  
  47. 129   
  48. 130         bios->data = kmalloc(bios->size, GFP_KERNEL);  
  49. 131         if (bios->data) {  
  50. 132                 for (i = 0; i < bios->size; i++)  
  51. 133                         nv_wo08(bios, i, nv_rd08(bios, 0x700000 + i));  
  52. 134         }  
  53. 135   
  54. 136 out:  
  55. 137         if (device->card_type >= NV_50)  
  56. 138                 nv_wr32(bios, 0x001700, bar0);  
  57. 139 }  

首先,这里先来看几个读写寄存器或其他东西的函数: nv_wo32 nv_wr32 [以32位的读为例,其他的类比很容易猜到] .

  1. // /drivers/gpu/drm/nouveau/core/include/core/subdev.h  
  2. 102 static inline void  
  3. 103 nv_wr32(void *obj, u32 addr, u32 data)  
  4. 104 {  
  5. 105         struct nouveau_subdev *subdev = nv_subdev(obj);  
  6. 106         nv_spam(subdev, "nv_wr32 0x%06x 0x%08x\n", addr, data);  
  7. 107         iowrite32_native(data, subdev->mmio + addr);  
  8. 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总线,其他另说.]

  1. // /drivers/gpu/drm/nouveau/core/include/core/object.h  
  2. 177 static inline void  
  3. 178 nv_wo32(void *obj, u64 addr, u32 data)  
  4. 179 {  
  5. 180         nv_spam(obj, "nv_wo32 0x%08llx 0x%08x\n", addr, data);  
  6. 181         nv_ofuncs(obj)->wr32(obj, addr, data);  
  7. 182 }  
这个是调用的obj的oclass里指示的函数指针wr32. 因此这个函数的作用可就是"因obj而异"了.例如,对于BIOS,这是向BIOS数据里写;对于instmem或gpuobj,这是向显存里写.

现在我们讨论BIOS当然就要看看BIOS的wr32函数指针:

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/base.c  
  2. 452 static void  
  3. 453 nouveau_bios_wr32(struct nouveau_object *object, u64 addr, u32 data)  
  4. 454 {  
  5. 455         struct nouveau_bios *bios = (void *)object;  
  6. 456         put_unaligned_le32(data, &bios->data[addr]);  
  7. 457 }  
bios->data就是从BIOS数据区里复制出来的BIOS数据,稍等一会我们就能看到它的初始化. 这个函数当然就是对其中的数据进行写.


回到nouveau_bios_shadow_pramin,第94行,上下文推断就是检测这种BIOS是否存在,不存在就返回.

第103行,通过看调试信息知道作用是判断这种BIOS是否启用了.

第107行,判断BIOS是否在显存内.

第112行,进行适当的mask,得到BIOS在显存中的地址. 当然有时候这个地址是无效的,那么在下面的if语句中,我们将使用默认的地址.

这里有一个寄存器出现了多次,就是0x1700,等会要讲的第118行还对其进行了mask操作,那么这个寄存器这个的作用是什么呢? 可以结合envytools的一段代码理解:

  1. // envytools /hwtest/vram.c  
  2. // line 28 ~ 43  
  3.   
  4. uint32_t vram_rd32(int card, uint64_t addr) {  
  5.     if (nva_cards[card]->chipset.card_type < 3) {  
  6.         return nva_rd32(card, 0x1000000 + addr);  
  7.     } else if (nva_cards[card]->chipset.card_type < 0x30) {  
  8.         return nva_grd32(nva_cards[card]->bar1, addr);  
  9.     } else if (nva_cards[card]->chipset.card_type < 0x50) {  
  10.         nva_wr32(card, 0x1570, addr);  
  11.         return nva_rd32(card, 0x1574);  
  12.     } else {  
  13.         uint32_t old = nva_rd32(card, 0x1700);  
  14.         nva_wr32(card, 0x1700, addr >> 16);  
  15.         uint32_t res = nva_rd32(card, 0x700000 | (addr & 0xffff));  
  16.         nva_wr32(card, 0x1700, old);  
  17.         return res;  
  18.     }  
  19. }  

函数名很明显是读取显存,刚刚我们获取这BIOS地址也是在显存里的,我们要想办法把他复制出来.

那么PCI设备的BAR不是已经给映射好了吗,为什么不用呢? 因为BAR映射的都是虚拟地址,现在我们完全还无法知道虚拟地址和物理地址的对应关系,这部分要到VM才被初始化. 所以我们只能想其他办法,来看看这个函数的实现吧. 首先判断显卡类型,这个我们只看NV50以后的.

首先把0x1700寄存器读出来存到old里,然后把addr >> 16 写进去,接着从0x700000 | (addr & 0xffff)中读出返回值,再把0x1700复原.

应该很容易理解这个0x1700和0x700000的作用了,0x1700决定0x700000 ~ 0x70ffff映射的物理地址,然后我们就能从中读取数据了.


那么回到第118行,再来看一下nv_mask吧:

  1. // /drivers/gpu/drm/nouveau/core/include/core/subdev.h  
  2. 110 static inline u32  
  3. 111 nv_mask(void *obj, u32 addr, u32 mask, u32 data)  
  4. 112 {  
  5. 113         u32 temp = nv_rd32(obj, addr);  
  6. 114         nv_wr32(obj, addr, (temp & ~mask) | data);  
  7. 115         return temp;  
  8. 116 }  
  9. 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的版本信息,找到就储存起来.

  1. // /drivers/gpu/drm/nouveau/core/subdev/bios/bit.c  
  2.  30 int  
  3.  31 bit_entry(struct nouveau_bios *bios, u8 id, struct bit_entry *bit)  
  4.  32 {  
  5.  33         if (likely(bios->bit_offset)) {  
  6.  34                 u8  entries = nv_ro08(bios, bios->bit_offset + 10);  
  7.  35                 u32 entry   = bios->bit_offset + 12;  
  8.  36                 while (entries--) {  
  9.  37                         if (nv_ro08(bios, entry + 0) == id) {  
  10.  38                                 bit->id      = nv_ro08(bios, entry + 0);  
  11.  39                                 bit->version = nv_ro08(bios, entry + 1);  
  12.  40                                 bit->length  = nv_ro16(bios, entry + 2);  
  13.  41                                 bit->offset  = nv_ro16(bios, entry + 4);  
  14.  42                                 return 0;  
  15.  43                         }  
  16.  44   
  17.  45                         entry += nv_ro08(bios, bios->bit_offset + 9);  
  18.  46                 }  
  19.  47   
  20.  48                 return -ENOENT;  
  21.  49         }  
  22.  50   
  23.  51         return -EINVAL;  
  24.  52 }  

第34行,先得到起始地址entry和个数entries.

第37行,遍历每一个entry,并且判断是否符合传入的id,然后返回之.


第501行,另一个if语句,在bit寻找失败的情况下判断bmp是否可用,可用就储存版本号.

第508行,输出调试信息,返回.


至此我们成功创建了第一个subdev,但我们并不会立即去执行它的init函数,在此之前,我们还要先执行devinit,i2c,gpio的ctor函数.

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值