注:由于我们主要是分析kitl的工作原理我们就电源管理的代码不做分析,以加电启动的程序流进行分析。
part1.
kitl初始化
-------------
Kitl的加载于其他调试服务之前,以提供为这些调试服务发布调试信息和接收主机调试命令的的通道。通常kitl在系统HAL初始化工作完成后进行加载,MS建议在OEMInit中启动kitl。这样就可以使用NIC或者是serial/Pal作为kitl的物理传输介质。
kitl的初始化由KitlInit完成,这部分代码主要负责:(to be fill later)
下面我们来看看kitl的具体代码,这些代码位于%CEROOT%/PRIVATE/WINCEOS/COREOS/NK/KITL下。
BOOL KitlInit (BOOL fStartKitl)
{
// just initialize function pointers
pKITLRegisterDfltClient = KITLRegisterDfltClient;
pKITLIoCtl = KITLIoctl;
pfnIsDesktopDbgrExist = IsDesktopDbgrExist;
// Initialize default clients
NewClient (KITL_SVC_DBGMSG, KITL_SVCNAME_DBGMSG, FALSE);
NewClient (KITL_SVC_PPSH, KITL_SVCNAME_PPSH, FALSE);
NewClient (KITL_SVC_KDBG, KITL_SVCNAME_KDBG, FALSE);
return fStartKitl? StartKitl (TRUE) : TRUE;
}
这段代码主要完成两个动作:
1.装载函数指针,为后续代码的执行装载入口点。
2.注册kitl客户端,这些客户端实现传输层以后就是我们所需要的调试界面。
输入参数决定是否立即启动KITL服务,如果false的话就仅仅进行初始化等待后续动作使用startKitl来启动kitl.
我们再来看看NewClient的原型
static PKITL_CLIENT NewClient (UCHAR uId, LPCSTR pszSvcName, BOOL fAlloc)
{
DEBUGCHK(IS_VALID_ID(uId));
DEBUGCHK (!KITLClients[uId]);
if (!fAlloc) {
DEBUGCHK(IS_DFLT_SVC(uId));
KITLClients[uId] = &DfltClnts[uId];
} else if (!(KITLClients[uId] = (PKITL_CLIENT) AllocMem (HEAP_KITLCLIENT))) {
return NULL;
}
memset (KITLClients[uId], 0, sizeof(KITL_CLIENT));
KITLClients[uId]->ServiceId = uId;
strcpy (KITLClients[uId]->ServiceName, pszSvcName);
return KITLClients[uId];
}
这个被称为NewClient的函数所完成的功能十分的简单,先检查所需要创建的结构是否是系统默认服务所需要的,如果是的话就直接将该结构的指针指向全局结构DfltClnts并初始化结构,如果不是就申请相应的空间完成该结构的初始化。默认的服务有 KITL_SVCNAME_DBGMSG, KITL_SVCNAME_PPSH, KITL_SVCNAME_KDBG分别对应Debug信息的发布通道(Debug message),文本控制台界面(PPshell),和内核调试界面(kernel debug),在这里大家可能会问:为什么不统一使用固定的全局结构来存放这些服务的信息呢?原因很简单,因为这些"client"在WindowSCE 下是可以注册扩充和注销的,这样用AllocMem所分配的内存空间在不再需要这些服务的时候可以释放掉,就可以避免不必要的浪费。另外 KITLClients是这样定义的PKITL_CLIENT KITLClients[MAX_KITL_CLIENTS];所以kitl所能注册的client连同3个默认的服务一共最多可以有 MAX_KITL_CLIENTS--128个。
下面继续沿着程序流往下看吧,kitlInit完成最基本的初始化动作即可启动kitl服务了。再看一下这个函数的原型。
static BOOL StartKitl (BOOL fInit)
{
// KITL already started?
if (!fInit && (KITLGlobalState & KITL_ST_DESKTOP_CONNECTED)) {
return TRUE;
}
/*
* When this function is called, the kernel hasn't yet been initialized,
* so can't make any system calls. Once the system has come up far
* enough to handle system calls, KITLInitializeInterrupt() is called to complete
* initialization. This is indicated by the KITL_ST_MULTITHREADED flag in KITLGlobalState.
*/
// Detect/initialize ethernet hardware, and return address information
if (!OEMKitlInit (&Kitl))
return FALSE;
// verify that the Kitl structure is initialized.
if (!Kitl.pfnDecode || !Kitl.pfnEncode || !Kitl.pfnEnableInt || !Kitl.pfnRecv || !Kitl.pfnSend
|| !Kitl.dwPhysBuffer || !Kitl.dwPhysBufLen || !Kitl.WindowSize || !Kitl.pfnGetDevCfg || !Kitl.pfnSetHostCfg) {
return FALSE;
}
// Validate that address is not in free RAM area - the HAL should put it in a reserved
// section of memory conditional on some environment var.
if ((pTOC->ulRAMStart < Kitl.dwPhysBuffer + Kitl.dwPhysBufLen)
&& (pTOC->ulRAMEnd > Kitl.dwPhysBuffer)) {
KITLOutputDebugString("/r/n!Debug Ethernet packet buffers in free RAM area - must set IMGEBOOT=1/r/n");
return FALSE;
}
if (Kitl.dwPhysBufLen < (DWORD) 3 * KITL_BUFFER_POOL_SIZE) {
KITLOutputDebugString("/r/n!Debug Ethernet buffer size too small, must be at least 0x%x bytes (3 * WindowSize * 2 * KITL_MTU)/r/n",
3 * KITL_BUFFER_POOL_SIZE);
return FALSE;
}
KITLGlobalState |= KITL_ST_KITLSTARTED; // indicate (to kdstub) that KITL has started
// If the initialized flag is already set, we are being called from the power on routine,
// so reinit the HW, but not any state.
if (!(KITLGlobalState & KITL_ST_ADAPTER_INITIALIZED)) {
// perform the initial handshake with the desktop
if (!KITLConnectToDesktop ()) {
KITLOutputDebugString ("/r/n!Unable to establish KITL connection with desktop!/r/n");
return FALSE;
}
// Set up kernel function pointers
pKITLInitializeInterrupt = KITLInitializeInterrupt;
pKITLSend = KITLSend;
pKITLRecv = KITLRecv;
KITLGlobalState |= KITL_ST_ADAPTER_INITIALIZED;
if (Kitl.dwBootFlags & KITL_FL_DBGMSG)
SetKernelCommDev (KERNEL_SVC_DBGMSG, KERNEL_COMM_ETHER);
if (Kitl.dwBootFlags & KITL_FL_PPSH)
SetKernelCommDev (KERNEL_SVC_PPSH, KERNEL_COMM_ETHER);
if (Kitl.dwBootFlags & KITL_FL_KDBG)
SetKernelCommDev (KERNEL_SVC_KDBG, KERNEL_COMM_ETHER);
// only perform cleanboot if it's connected at boot. Cleanboot flag is
// ignored if it's started dynamically.
if (fInit && (Kitl.dwBootFlags & KITL_FL_CLEANBOOT)) {
extern ROMHDR *const volatile pTOC; // Gets replaced by RomLoader with real address
// just clear the magic nOEMKitlInitumber (see SC_SetCleanRebootFlag)
// NOTE: We can NOT call SC_SetCleanRebootFlag here since logPtr isn't
// initialized yet.
((fslog_t *)((pTOC->ulRAMFree + MemForPT) | 0x20000000))->magic1 = 0;
}
// if OEM calls KitlInit (FALSE), KITLInitializeInterrupt will
// not be called in SystemStartupFuc. We need to initialize
// interrupt here (when RegisterClient is called)
if (fKITLcsInitialized && !InSysCall ()) {
KITLInitializeInterrupt ();
}
}
LOG (KITLGlobalState);
return TRUE;
}
启动代码首先判断是否已经启动kitl服务,之后调用OEMKitlInit,该函数并不在private目录下实现,通常windowsCE需要用户定制的代码都是这种结构---MS提供的代码接口,用户自己完成相应的OEM部分,通常这些代码都是与具体的硬件平台相关的代码。kitl的OEM 代码在HAL中实现,通常在platform/kernel/hal/.下,这部分的代码我们先跳过,看完startkitl的全貌再回过头逐个说明。 OEMkitlInit为kitl初始化硬件传输介质,同时分配初始化一些kitl所需要的全局结构。随后startkitl继续检查 OEMkitlInit所分配和初始化的KITL结构和内存区域是否有效后设置kitl的全局标示KITL_ST_KITLSTARTED;之后设置终端服务程序以及接收和发送程序的入口点后设置全局标示KITL_ST_ADAPTER_INITIALIZED。现在传输介质已经全部就绪,通过 SetKernelCommDev设置kernel通过ethernet传送调试信息,调试输入,以及CESH控制台。再后调用 KITLInitializeInterrupt完成中断的初始化kitl启动的过程就结束了。
紧接着我们来看看,OEMkitlInit都须要我们干什么。下面用SMDK2440的kitl为实例来进行分析:
BOOL OEMKitlInit (PKITLTRANSPORT pKitl)
{
KITLOutputDebugString ("+OEMKitlInit/n");
RETAILMSG(1, (_T("+OEMKitlInit/r/n")));
// try to find a transport available
if (!InitEther (pKitl)
&& !InitParallelSerial (pKitl)) {
KITLOutputDebugString ("Unable to initialize KITL Transports!/n");
return FALSE;
}
gpKitl = pKitl;
KITLOutputDebugString ("-OEMKitlInit/n");
RETAILMSG(1, (_T("-OEMKitlInit/r/n")));
return TRUE;
}
事实上工作很简单,调用InitEther (pKitl) 和 !InitParallelSerial (pKitl)初始化网卡直接把初始化的KITL全局结构返回就是所有的工作。这儿的InitParallelSerial是一个dummy永远返回 false,也就是说这里没有对serial¶llel transport进行支持。真正的工作量集中在InitEther之后。事实上InitEther 和 InitParallelSerial只要任意的实现一个就可以达到建立传输界面的目的.下面,我们继续看后面的代码。
BOOL InitEther(PKITLTRANSPORT pKitl)
{
EDBG_ADAPTER adp;
DWORD dwDHCPLeaseTime;
DWORD dwSubnetMask;
KITLOutputDebugString ("+InitEther/n");
memset (&adp, 0, sizeof(adp));
memset (pKitl, 0, sizeof (KITLTRANSPORT));
// use existing code for ether initialization
if (!OEMEthInit (&adp))
return FALSE;
// we are going to completely ignore the info in bootargs and the adaptor info
// returned from OEMEthInit, except MAC address. Just to prove that KITL will connect standalone
// get the MAC address
MyAddr.wMAC[0] = adp.Addr.wMAC[0];
MyAddr.wMAC[1] = adp.Addr.wMAC[1];
MyAddr.wMAC[2] = adp.Addr.wMAC[2];
//MyAddr = adp.Addr;
CreateDeviceName(&MyAddr, pKitl->szName);
KITLOutputDebugString ("Using device name: %s/n", pKitl->szName);
// If we haven't been given an IP address from our loader (or if we're not using static IP), get an IP address
// from a DHCP server.
if (adp.Addr.dwIP)
{
// Static IP or we got the IP from our bootloader...
MyAddr.dwIP = adp.Addr.dwIP;
dwSubnetMask = 0; // Don't care about subnet mask...
dwDHCPLeaseTime = adp.DHCPLeaseTime;
}
else
{
// Get a DHCP address...
if (!EbootGetDHCPAddr (&MyAddr, &dwSubnetMask, &dwDHCPLeaseTime))
return FALSE;
}
MyAddr.wPort = htons (EDBG_SVC_PORT);
KITLOutputDebugString ("Device %s, IP %s, Port %d/n", pKitl->szName, inet_ntoa (MyAddr.dwIP), htons (MyAddr.wPort));
// initialize KITL Ethernet transport layer
if (!KitlEtherInit (&MyAddr, dwDHCPLeaseTime)) {
KITLOutputDebugString ("Unable to initialize KITL Ether transport/n");
return FALSE;
}
// fill in the blanks in KITLTRANSPORT structure.
pKitl->FrmHdrSize = KitlEtherGetFrameHdrSize ();
pKitl->Interrupt = (UCHAR) adp.SysIntrVal;
pKitl->dwPhysBuffer = EDBG_PHYSICAL_MEMORY_START;
pKitl->dwPhysBufLen = 0x20000; // 128K of buffer available
pKitl->dwBootFlags = 0;
pKitl->WindowSize = EDBG_WINDOW_SIZE;
pKitl->pfnDecode = KitlEtherDecodeUDP;
pKitl->pfnEncode = KitlEtherEncodeUDP;
pKitl->pfnSend = EthSend;
pKitl->pfnRecv = OEMEthGetFrame;
pKitl->pfnEnableInt = KitlEthEnableInts;
pKitl->pfnSetHostCfg = SetHostCfg;
pKitl->pfnGetDevCfg = GetDevCfg;
KITLOutputDebugString ("-InitEther/n");
return TRUE;
}
这个函数完成的工作主要是调用OEMEthInit初始化网卡的服务程序及获得相应的IP和MAC,如果IP无效则用DHCP动态获得IP.通过 MAC值产生一个标示,这个标示用来给PB的IDE使用。刚才的我们在kitlInit中看到除了检查OEMkitlInit的返回值之外还检查了 KITL结构,该结构的这些特征值正是在这儿设置的。在这儿可以看到pKitl->pfnDecode pKitl->pfnEncode pKitl->pfnSetHostCfg pKitl->pfnGetDevCfg 以及kitl所用的中断号这些都是OEM代码,也就是用于传输的编码和解码形式以及配置函数都是可以自己定义的,这样一来也就无所谓使用什么传输介质作为 KITK
的transport了,这就为使用1394或者是USB这一类的传输链路也能充当传输界面作了准备。