查看: 4848|回复: 11

[交流] 从变色龙源码分析ACER 4741G HM55主板芯片组的睡眠问题

[复制链接]
suhetao 发表于 2011-11-17 16:11 | 显示全部楼层 |阅读模式
快御云安全
本帖最后由 suhetao 于 2011-11-17 17:45 编辑

    写在前面的话:鄙人是一个不折不扣的IT民工,整天“被沉浸”在无休无止的工作当中,这里写下的一切一切仅仅是忙里偷闲所致,如有不当,敬请原谅。
    因为苹果操作系统完善的内存和磁盘管理,快捷迅速的任务响应机制,所以对这个操作系统情有独钟,至于苹果,史上最差的性价比,不敢恭维。一直都想让自己的笔记本安装上苹果操作系统,其实是因为近来在做I疯的一些开发工作,需要XCODE这一仅仅运行在MAC平台的开发工具。无奈,只能下个安装光盘镜像在4741G上安装,一切顺利,唯独睡眠唤醒问题一直很纠结。难道这是4741G的通病?通过对DSDT打EHCI补丁,依旧无效,一气之下删除了所有有关EHCI相关的ASL代码,却意外的可以睡眠了,但是却无法正常唤醒,唤醒症状是黑屏,但是各指示灯正常以及硬盘已经启动。船到桥头自然直,想起了变色龙的USB FIX选项,在org.chameleon.Boot.plist添加如下选项。
  1. <key>EHCIacquire</key>
  2. <string>Yes</string>
  3. <key>USBLegacyOff</key>
  4. <string>Yes</string>
复制代码
睡眠唤醒正常了。到底这两个神奇的选项为什么可以化腐朽为传奇,引起了鄙人的高度的重视。二话不说,下载了变色龙的源代码,稍稍分析了一下,整个变色龙项目编译以后成为一个boot的文件,因此猜测源代码中的boot.c文件类似于Main函数所在,不失所望。一切从boot函数开始。

  1. //==========================================================================
  2. // This is the entrypoint from real-mode which functions exactly as it did
  3. // before. Multiboot does its own runtime initialization, does some of its
  4. // own things, and then calls common_boot.
  5. void boot(int biosdev)
  6. {
  7.         initialize_runtime();
  8.         // Enable A20 gate before accessing memory above 1Mb.
  9.         enableA20();
  10.         common_boot(biosdev);
  11. }
复制代码
其中common_boot函数有以下一段
  1. if (ret <= 0) {
  2.                         printf("Can't find %s\n", bootFile);
  3.                         sleep(1);
  4.                         
  5.                         if (gBootFileType == kNetworkDeviceType) {
  6.                                 // Return control back to PXE. Don't unload PXE base code.
  7.                                 gUnloadPXEOnExit = false;
  8.                                 break;
  9.                         }
  10.                         pause();

  11.                 } else {
  12.                         /* Won't return if successful. */
  13.                         ret = ExecKernel(binary);
  14.                 }
复制代码
我们想要的答案就隐藏在ExecKernel函数中, ExecKernel有以下这么一段
  1. ......
  2. usb_loop();
  3. ......
复制代码
usb_loop这个函数包含在变色龙源码usb.c文件中,内容如下

  1. // Loop through the list and call the apropriate patch function
  2. int usb_loop()
  3. {
  4.         int retVal = 1;
  5.         bool fix_ehci, fix_uhci, fix_usb, fix_legacy;
  6.         fix_ehci = fix_uhci = fix_usb = fix_legacy = false;
  7.         
  8.         if (getBoolForKey(kUSBBusFix, &fix_usb, &bootInfo->chameleonConfig))
  9.         {
  10.                 fix_ehci = fix_uhci = fix_legacy = fix_usb;        // Disable all if none set
  11.         }
  12.         else
  13.         {
  14.                 getBoolForKey(kEHCIacquire, &fix_ehci, &bootInfo->chameleonConfig);
  15.                 getBoolForKey(kUHCIreset, &fix_uhci, &bootInfo->chameleonConfig);
  16.                 getBoolForKey(kLegacyOff, &fix_legacy, &bootInfo->chameleonConfig);
  17.         }
  18.         
  19.         struct pciList* current = usbList;
  20.         
  21.         while(current)
  22.         {
  23.                 switch (pci_config_read8(current->pciDev->dev.addr, PCI_CLASS_PROG))
  24.                 {
  25.                         // EHCI
  26.                         case 0x20:
  27.                             if(fix_ehci)   retVal &= ehci_acquire(current->pciDev);
  28.                             if(fix_legacy) retVal &= legacy_off(current->pciDev);
  29.                                 
  30.                                 break;
  31.                                 
  32.                         // UHCI
  33.                         case 0x00:
  34.                                 if (fix_uhci) retVal &= uhci_reset(current->pciDev);

  35.                                 break;
  36.                 }
  37.                
  38.                 current = current->next;
  39.         }
  40.         return retVal;
  41. }
复制代码
usb_loop函数通过getBoolForKey函数读取org.chameleon.Boot.plist文件中相关的USB FIX设定,然后遍历所有的EHCI设备,执行ehci_acquire,legacy_off这两个函数。(仅仅针对上面提到的“添加如下选项”的情况)所有的谜团已经解开,真相只有一个,先来看看legacy_off函数的源代码

  1. int legacy_off (pci_dt_t *pci_dev)
  2. {
  3.         // Set usb legacy off modification by Signal64
  4.         // NOTE: This *must* be called after the last file is loaded from the drive in the event that we are booting form usb.
  5.         // NOTE2: This should be called after any getc()/getchar() call. (aka, after the Wait=y keyworkd is used)
  6.         // AKA: Make this run immediatly before the kernel is called
  7.         uint32_t        capaddr, opaddr;                  
  8.         uint8_t                eecp;                        
  9.         uint32_t        usbcmd, usbsts, usbintr;                        
  10.         uint32_t        usblegsup, usblegctlsts;               
  11.         
  12.         int isOSowned;
  13.         int isBIOSowned;
  14.         
  15.         verbose("Setting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n",
  16.                         pci_dev->vendor_id, pci_dev->device_id,
  17.                         pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);
  18.         
  19.         
  20.         // capaddr = Capability Registers = dev.addr + offset stored in dev.addr + 0x10 (USBBASE)
  21.         capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);        
  22.         
  23.         // opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)
  24.         opaddr = capaddr + *((unsigned char*)(capaddr));                 
  25.         
  26.         // eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8
  27.         eecp=*((unsigned char*)(capaddr + 9));
  28.         
  29.         DBG("capaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);
  30.         
  31.         usbcmd = *((unsigned int*)(opaddr));                        // Command Register
  32.         usbsts = *((unsigned int*)(opaddr + 4));                // Status Register
  33.         usbintr = *((unsigned int*)(opaddr + 8));                // Interrupt Enable Register
  34.         
  35.         DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
  36.         
  37.         // read PCI Config 32bit USBLEGSUP (eecp+0)
  38.         usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
  39.         
  40.         // informational only
  41.         isBIOSowned = !!((usblegsup) & (1 << (16)));
  42.         isOSowned = !!((usblegsup) & (1 << (24)));
  43.         
  44.         // read PCI Config 32bit USBLEGCTLSTS (eecp+4)
  45.         usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
  46.         
  47.         DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
  48.         
  49.         // Reset registers to Legacy OFF
  50.         DBG("Clearing USBLEGCTLSTS\n");
  51.         pci_config_write32(pci_dev->dev.addr, eecp + 4, 0);        //usblegctlsts
  52.         
  53.         // if delay value is in milliseconds it doesn't appear to work.
  54.         // setting value to anything up to 65535 does not add the expected delay here.
  55.         delay(100);
  56.         
  57.         usbcmd = *((unsigned int*)(opaddr));
  58.         usbsts = *((unsigned int*)(opaddr + 4));
  59.         usbintr = *((unsigned int*)(opaddr + 8));
  60.         
  61.         DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
  62.         
  63.         DBG("Clearing Registers\n");
  64.         
  65.         // clear registers to default
  66.         usbcmd = (usbcmd & 0xffffff00);
  67.         *((unsigned int*)(opaddr)) = usbcmd;
  68.         *((unsigned int*)(opaddr + 8)) = 0;                                        //usbintr - clear interrupt registers
  69.         *((unsigned int*)(opaddr + 4)) = 0x1000;                        //usbsts - clear status registers         
  70.         pci_config_write32(pci_dev->dev.addr, eecp, 1);                //usblegsup
  71.         
  72.         // get the results
  73.         usbcmd = *((unsigned int*)(opaddr));
  74.         usbsts = *((unsigned int*)(opaddr + 4));
  75.         usbintr = *((unsigned int*)(opaddr + 8));
  76.         
  77.         DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
  78.         
  79.         // read 32bit USBLEGSUP (eecp+0)
  80.         usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
  81.         
  82.         // informational only
  83.         isBIOSowned = !!((usblegsup) & (1 << (16)));
  84.         isOSowned = !!((usblegsup) & (1 << (24)));
  85.         
  86.         // read 32bit USBLEGCTLSTS (eecp+4)
  87.         usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
  88.         
  89.         DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
  90.         
  91.         verbose("Legacy USB Off Done\n");        
  92.         return 1;
  93. }
复制代码
这些到底是什么天书?配合一下INTEL发布的EHCI参考文档,其实都很简单。先上个图,让大家熟悉一下EHCI的组织架构
EHCI.JPG
可以看到通过设备地址我们先到到达一个PCI CONFIGURATION寄存器的区域,而其中的USB BASE ADDRESS指向的就是MMIO地址,也就是CAPABILITY REGISTERS和OPERATIONAL REGISTERS.
再来几张图看看PCI CONFIGURATION寄存器以及CAPABILITY、OPERATIONAL REGISTERS的具体说明
PCI_CONFIG.JPG
以及
CREGISTER.JPG
还有
OREGISTER.JPG
其中EECP在HCCPARAMS寄存器中指定
EECP.JPG
最后是CAPABILITY、OPERATIONAL REGISTERS之间的关系。
REGISTERS.JPG
有了这些说明图片legacy_off函数就很好理解了。首先legacy_off函数从设备地址的偏移量0x10读取USBBASE地址访问CAPABILITY REGISTER,然后CAPABILITY REGISTER偏移CAPLENGTH个长度就是OPERATIONAL REGISTER。有了所有寄存器的地址,就很好处理了。首先对USBLEGCTLSTS寄存器清零(EECP+4),相当于USBLEGCTLSTS寄存器RESET(默认全是0),延迟100毫秒等待寄存器RESET完毕,然后RESET USBCMD,通过对寄存器[0,1]两个位赋0值,通过设置USBINTR为0关闭USB中断,以及重置USB状态寄存器的USBSTS。剩下的就是读取一些信息。整个legacy_off函数的作用相当于重置USB设置。
再来看看ehci_acquire函数。代码如下

  1. int ehci_acquire (pci_dt_t *pci_dev)
  2. {
  3.         int                j, k;
  4.         uint32_t        base;
  5.         uint8_t                eecp;
  6.         uint8_t                legacy[8];
  7.         bool                isOwnershipConflict;        
  8.         bool                alwaysHardBIOSReset;

  9.         alwaysHardBIOSReset = false;        
  10.         if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->chameleonConfig)) {
  11.                 alwaysHardBIOSReset = true;
  12.         }

  13.         pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);
  14.         base = pci_config_read32(pci_dev->dev.addr, 0x10);

  15.         verbose("EHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n",
  16.                 pci_dev->vendor_id, pci_dev->device_id,
  17.                 pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
  18.                 base);

  19.         if (*((unsigned char*)base) < 0xc)
  20.         {
  21.                 DBG("Config space too small: no legacy implementation\n");
  22.                 return 1;
  23.         }
  24.         eecp = *((unsigned char*)(base + 9));
  25.         if (!eecp) {
  26.                 DBG("No extended capabilities: no legacy implementation\n");
  27.                 return 1;
  28.         }

  29.         DBG("eecp=%x\n",eecp);

  30.         // bad way to do it
  31.         // pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);
  32.         for (j = 0; j < 8; j++) {
  33.                 legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  34.                 DBG("%02x ", legacy[j]);
  35.         }
  36.         DBG("\n");

  37.         //Real Job: based on orByte's AppleUSBEHCI.cpp
  38.         //We try soft reset first - some systems hang on reboot with hard reset
  39.         // Definitely needed during reboot on 10.4.6

  40.         isOwnershipConflict = ((legacy[3] & 1 !=  0) && (legacy[2] & 1 !=  0));
  41.         if (!alwaysHardBIOSReset && isOwnershipConflict) {
  42.                 DBG("EHCI - Ownership conflict - attempting soft reset ...\n");
  43.                 DBG("EHCI - toggle OS Ownership to 0\n");
  44.                 pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);
  45.                 for (k = 0; k < 25; k++) {
  46.                         for (j = 0; j < 8; j++) {
  47.                                 legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  48.                         }
  49.                         if (legacy[3] == 0) {
  50.                                 break;
  51.                         }
  52.                         delay(10);
  53.                 }
  54.         }        

  55.         DBG("Found USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);
  56.         pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);

  57.         // wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear
  58.         for (k = 0; k < 25; k++) {
  59.                 for (j = 0;j < 8; j++) {
  60.                         legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  61.                 }
  62.                 DBG ("%x:%x,",legacy[3],legacy[2]);
  63.                 if (legacy[2] == 0) {
  64.                         break;
  65.                 }
  66.                 delay(10);
  67.         }

  68.         for (j = 0;j < 8; j++) {
  69.                 legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  70.         }
  71.         isOwnershipConflict = ((legacy[2]) != 0);
  72.         if (isOwnershipConflict) {
  73.                 // Soft reset has failed. Assume SMI being ignored
  74.                 // Hard reset
  75.                 // Force Clear BIOS BIT
  76.                 DBG("EHCI - Ownership conflict - attempting hard reset ...\n");                        
  77.                 DBG ("%x:%x\n",legacy[3],legacy[2]);
  78.                 DBG("EHCI - Force BIOS Ownership to 0\n");

  79.                 pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);
  80.                 for (k = 0; k < 25; k++) {
  81.                         for (j = 0; j < 8; j++) {
  82.                                 legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  83.                         }
  84.                         DBG ("%x:%x,",legacy[3],legacy[2]);

  85.                         if ((legacy[2]) == 0) {
  86.                                 break;
  87.                         }
  88.                         delay(10);        
  89.                 }               
  90.                 // Disable further SMI events
  91.                 for (j = 4; j < 8; j++) {
  92.                         pci_config_write8(pci_dev->dev.addr, eecp + j, 0);
  93.                 }
  94.         }

  95.         for (j = 0; j < 8; j++) {
  96.                 legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
  97.         }

  98.         DBG ("%x:%x\n",legacy[3],legacy[2]);

  99.         // Final Ownership Resolution Check...
  100.         if (legacy[2] & 1) {                                       
  101.                 DBG("EHCI controller unable to take control from BIOS\n");
  102.                 return 0;
  103.         }

  104.         DBG("EHCI Acquire OS Ownership done\n");        
  105.         return 1;
  106. }
复制代码
对PCI_CONFIGURATION偏移量0x04的地方写入0x0002,可惜没有主板的详细开发文档,而且INTEL提供的EHCI只提到说00&#8722;08h,Register implementation as needed for specific PCI device。所以具体作用不得而知,其实鄙人也不相信这个有啥用。然后剩下的部分就是EHCI的Ownership修复。
所以整体上看,只要进行Ownership修复,以及重置USB相关寄存器,就可以使HM55的睡眠唤醒正常。
若想详细了解以上所说函数中对USB相关寄存器地址以及设定的具体值含义请查阅INTEL官方的EHCI文档《Enhanced Host Controller Interface Specification for Universal Serial Bus 》,我就不再复述。今天就说到这里吧。抱砖引玉,希望大家少扔点鸡蛋。等这段子忙完看看,能否单纯在DSDT中添加相应的Ownership fix以及USB reset来修复HM55的睡眠唤醒问题,而不需要在变蛇龙引导参数中添加相关的USB设定。

评分

8

查看全部评分

sensssz 发表于 2011-11-17 16:23 | 显示全部楼层
膜拜大神……
回复

使用道具 举报

头像被屏蔽
lihaotv2004 发表于 2011-11-17 16:45 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

lhongj 发表于 2011-11-17 17:30 | 显示全部楼层
管用。本来睡眠唤醒黑屏,现在是睡眠唤醒花屏,能看见东西了
回复

使用道具 举报

FisherMac 发表于 2011-11-17 17:32 | 显示全部楼层
楼主图挂了..用256kb以下附件上传图片吧
回复

使用道具 举报

hjyun060872 发表于 2011-11-17 17:33 | 显示全部楼层
直接添加就行了吗?看这,高手啊
回复

使用道具 举报

suhetao  楼主| 发表于 2011-11-17 17:50 | 显示全部楼层
下班前修复了一下图片的链接。简而言之,就和我文章中最后说的一样。要启用HM55的睡眠唤醒功能,Ownership fix以及USB reset是必须的。
简单的说就是在变色龙配置文件添加选项
<key>EHCIacquire</key>
<string>Yes</string>
<key>USBLegacyOff</key>
<string>Yes</string>
以及在DSDT ASL中删除所有USB相关的代码,就可以使HM55唤醒睡眠正常。
上面只是原理性的探讨。
回复

使用道具 举报

mingming0206 发表于 2011-11-17 18:06 | 显示全部楼层
却是高深呀
回复

使用道具 举报

道奇兔 发表于 2011-11-17 18:11 | 显示全部楼层
suhetao 发表于 2011-11-17 17:50
下班前修复了一下图片的链接。简而言之,就和我文章中最后说的一样。要启用HM55的睡眠唤醒功能,Ownership  ...

說穿了dsdt也是可以...這部分說明我就省略了

其實  95%的人都忽略了睡眠BIOS的關係(mac只有三種睡眠....不像win)
回复

使用道具 举报

crazybirdy 发表于 2011-11-17 18:38 | 显示全部楼层
通过对DSDT打EHCI补丁,依旧无效,一气之下删除了所有有关EHCI相关的ASL代码,却意外的可以睡眠了,但是却无法正常唤醒,唤醒症状是黑屏,但是各指示灯正常以及硬盘已经启动。
以前我的P5B一直不愿意睡眠,总是一睡就醒,完全不断电,完全是USB不断电在搞鬼
后来只留下/Extra/SleepEnabler.kext + UHC1~6 + EHC1~2 的USB内建dsdt,其他USB dsdt都删掉
就可以正常睡眠了,睡眠中,主机+显示器.完全断电,不过只能按电源键唤醒睡眠。睡醒之后一切正常。

倒是没用到变色龙的USB修正功能

            Device (UHC1)
            {
                Name (_ADR, 0x001D0000)
                Method (_DSM, 4, NotSerialized)
                {
                    Store (Package (0x02)
                        {
                            "device-id",
                            Buffer (0x04)
                            {
                                0x34, 0x3A, 0x00, 0x00
                            }
                        }, Local0)
                    DTGP (Arg0, Arg1, Arg2, Arg3, RefOf (Local0))
                    Return (Local0)
                }
            }

回复

使用道具 举报

xinjiemail 发表于 2011-11-17 18:46 | 显示全部楼层
都是高手啊,真不错这贴。兔版应该加到资源集合贴去
回复

使用道具 举报

zshwq5 发表于 2011-11-19 00:12 | 显示全部楼层
技术帖收藏先,能有HM65芯片组的修复就更好 了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋手机版联系我们

Copyright © 2005-2025 PCBeta. All rights reserved.

Powered by Discuz!  CDN加速及安全服务由「快御」提供

请勿发布违反中华人民共和国法律法规的言论,会员观点不代表远景论坛官方立场。

远景在线 ( 苏ICP备17027154号 )|远景论坛 |Win11论坛 |Win10论坛 |Win8论坛 |Win7论坛 |WP论坛 |Office论坛

GMT+8, 2025-1-4 13:46

快速回复 返回顶部 返回列表