12/27/2011

Linux ハードウェア デバッグコマンド その2

昨日は力尽きたので、今日投稿するっす。
先週に引き続き、Linuxでユーザーランドからの物理メモリの読み書きコマンドです。
mmapを使って仮想メモリ経由で読み書きするようにしてみました。

例によってソースを晒しておきます。


/*
 * mem.c
 * ver.0.1:  Dec 26, 2011  S.Ishihara
 */


#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>




#define DEV_PATH "/dev/mem"


int
main(int argc, char *argv[])
{
    int             opt;
    extern char     *optarg;
    extern int      optind, opterr;
    int             width = 1;  /* default byte access */
    unsigned int    memaddr, wdata;
    unsigned int    pgoffset, pgaddr;
    unsigned int    pagesize = sysconf(_SC_PAGESIZE);
    unsigned char   *p;
    int             fd;


    while ((opt = getopt(argc, argv, "w:")) != -1) {
        if (opt == 'w') {
            width = atoi(optarg);
        } else {
            goto error;
        }
    }


    argc -= optind;
    argv += optind;


    fd = open(DEV_PATH, O_RDWR);
    if (fd <= 0) {
        fprintf(stderr, "open error: %s\n", DEV_PATH);
        return 1;
    }


    if (argc == 1) {
        /* Read Mem */
        memaddr  = strtoul(argv[0], NULL, 16);
        pgoffset = memaddr & (pagesize -1);
        pgaddr   = memaddr & ~(pagesize -1);
        p = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, pgaddr);
        if (p < 0) {
            fprintf(stderr, "mmap error\n");
            return 1;
        }
        if (width == 1) {
            printf("0x%08x: 0x%02x\n", memaddr, *(p + pgoffset));
        } else if (width == 2) {
            printf("0x%08x: 0x%04x\n", memaddr, *((unsigned short *)(p + pgoffset)));
        } else if (width == 4) {
            printf("0x%08x: 0x%08x\n", memaddr, *((unsigned int *)(p + pgoffset)));
        } else {
            goto error;
        }
    } else if (argc == 2) {
        /* Write Mem */
        memaddr  = strtoul(argv[0], NULL, 16);
        pgoffset = memaddr & (pagesize -1);
        pgaddr   = memaddr & ~(pagesize -1);
        p = mmap(NULL, pagesize, PROT_WRITE, MAP_SHARED, fd, pgaddr);
        if (p < 0) {
            fprintf(stderr, "mmap error\n");
            return 1;
        }
        wdata  = strtoul(argv[1], NULL, 16);
        if (width == 1) {
            *(p + pgoffset) = (unsigned char)wdata;
        } else if(width == 2) {
            *((unsigned short *)(p + pgoffset)) = (unsigned short)wdata;
        } else if(width == 4) {
            *((unsigned int *)(p + pgoffset)) = (unsigned int)wdata;
        } else {
            goto error;
        }
    } else {
        goto error;
    }
    munmap(p, pagesize);
    close(fd);
    return 0;


error:
    printf("Usage: Mem [-w WIDTH] ADDRESS [DATA]\n"
            "Mem read or write.\n"
            "  -w        number of byte width. permit 1(default), 2, 4\n"
            "\n"
            "This command executable only root user.\n"
            "Mem address possible range 32bit.\n"
            "\n"
            "Examples:\n"
            "  Mem a0000               Read memory from address 0xa0000.\n"
            "  Mem a0000 31            Write memory address 0xa0000 to 0x31.\n"
            "  Mem -w4 20000 5a5a5a5a  Write memory address 0x20000 to 0x5a5a5a5a.\n"
            "\n");
    return 1;
}


試行錯誤しながら、ごそっと書いたので、冗長だったり、手抜きな部分もありますが、ぼちぼち動作するので、とりあえずよしとします。
目的としては、PCIデバイスのMMIOを直接読み書きしたいわけなんですが、あんまし真面目に動作確認できてないっす。使いながら確認してみます。

12/19/2011

Linux ハードウェア デバッグコマンド その1

今日は、Tx50の開発はお休みして、表題の件にちょっと時間を割くことにしました。

なんというか、本業のほうで、やっかいなデバッグをやることになりまして、
Linuxからハードウェアの状態を手軽に知る術が欲しくなって、コマンド作ることにしました。本業では結果を出すのに追われて、なかなか腰を据えてこういった開発補助ツールを作ることに時間が割けないわけです。

ユーザーランドからハードウェアに直接アクセスすることは、なにかと問題があったりで、本来ドライバを作るべきですが、開発段階でハードウェアの状態をちょっと確認したいというときは、さくっとコマンドで済ませたいところです。

というわけで、今回はユーザーランドから直接I/OとPCIのコンフィグレーション空間を読み書きできるコマンドを作成します。ホントは物理メモリとPCI Expressの拡張コンフィグレーション空間の読み書きコマンドも作りたかったんですが、時間が足りなかったので、それはまた今度にします。

で、おもむろにソースです。たぶん組み込み開発でLinux使ったプロジェクトのハード屋さんには、需要があるんじゃなかろうかと思いソースを晒しときます。(自分が会社からソースをゲットする目的もありますが)


/*
 * io.c
 * ver.0.1:  Dec 18, 2011  S.Ishihara
 */


#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>


int
main(int argc, char *argv[])
{
    int             opt;
    extern char     *optarg;
    extern int      optind, opterr;
    int             width = 1;  /* default byte access */
    unsigned short  ioaddr;
    unsigned int    wdata;


    while ((opt = getopt(argc, argv, "w:")) != -1) {
        if (opt == 'w') {
            width = atoi(optarg);
        } else {
            goto error;
        }
    }


    argc -= optind;
    argv += optind;


    iopl(3);


    if (argc == 1) {
        /* Read I/O */
        ioaddr = strtoul(argv[0], NULL, 16);
        if (width == 1) {
            printf("0x%04x: 0x%02x\n", ioaddr, inb(ioaddr));
        } else if (width == 2) {
            printf("0x%04x: 0x%04x\n", ioaddr, inw(ioaddr));
        } else if (width == 4) {
            printf("0x%04x: 0x%08x\n", ioaddr, inl(ioaddr));
        } else {
            goto error;
        }
    } else if (argc == 2) {
        /* Write I/O */
        ioaddr = strtoul(argv[0], NULL, 16);
        wdata  = strtoul(argv[1], NULL, 16);
        if (width == 1) {
            outb(wdata, ioaddr);
        } else if(width == 2) {
            outw(wdata, ioaddr);
        } else if(width == 4) {
            outl(wdata, ioaddr);
        } else {
            goto error;
        }
    } else {
        goto error;
    }
    return 0;


error:
    printf("Usage: io [-w WIDTH] ADDRESS [DATA]\n"
            "I/O read or write.\n"
            "  -w        number of byte width. permit 1(default), 2, 4\n"
            "\n"
            "This command executable only root user.\n"
            "I/O address possible range 16bit.\n"
            "\n"
            "Examples:\n"
            "  io 3f8               Read I/O from address 0x3f8.\n"
            "  io 3f8 0x31          Write I/O address 0x3f8 to 0x31.\n"
            "  io -w4 cf8 80000000  Write I/O address 0xcf8 to 0x80000000 double word.\n"
            "\n");
    return 1;
}





/*
 * pci.c
 * ver.0.1:  Dec 18, 2011  S.Ishihara
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>

#define PCI_INDEX 0xcf8
#define PCI_DATA  0xcfc

unsigned char
pciRead8(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    return inb(PCI_DATA + (reg & 0x03));
}

unsigned short
pciRead16(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    return inw(PCI_DATA + (reg & 0x02));
}

unsigned int
pciRead32(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    return inl(PCI_DATA);
}

void
pciWrite8(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg, unsigned char value)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    outb(value, PCI_DATA + (reg & 0x03));
}

void
pciWrite16(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg, unsigned short value)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    outw(value, PCI_DATA + (reg & 0x02));
}

void
pciWrite32(unsigned char bus, unsigned char dev, unsigned char fnc, unsigned char reg, unsigned int value)
{
    outl((1 << 31) + (bus << 16) + ((dev & 0x1f) << 11) +\
            ((fnc & 0x07) << 8) + (reg & ~(0x03)), PCI_INDEX);
    outl(value, PCI_DATA);
}


int
main(int argc, char *argv[])
{
    int             opt;
    extern char     *optarg;
    extern int      optind, opterr;
    int             width = 1;  /* default byte access */
    unsigned char   bus, dev, fnc, reg;
    unsigned int    wdata;

    while ((opt = getopt(argc, argv, "w:")) != -1) {
        if (opt == 'w') {
            width = atoi(optarg);
        } else {
            goto error;
        }
    }

    argc -= optind;
    argv += optind;

    iopl(3);

    if (argc == 4) {
        /* Read Pci */
        bus = strtoul(argv[0], NULL, 16);
        dev = strtoul(argv[1], NULL, 16);
        fnc = strtoul(argv[2], NULL, 16);
        reg = strtoul(argv[3], NULL, 16);
        if (width == 1) {
            printf("B:0x%02x/ D:0x%02x/ F:0x%02x/ R:0x%02x: 0x%02x\n",
                    bus, dev, fnc, reg, pciRead8(bus, dev, fnc, reg));
        } else if (width == 2) {
            printf("B:0x%02x/ D:0x%02x/ F:0x%02x/ R:0x%02x: 0x%04x\n",
                    bus, dev, fnc, reg, pciRead16(bus, dev, fnc, reg));
        } else if (width == 4) {
            printf("B:0x%02x/ D:0x%02x/ F:0x%02x/ R:0x%02x: 0x%08x\n",
                    bus, dev, fnc, reg, pciRead32(bus, dev, fnc, reg));
        } else {
            goto error;
        }
    } else if (argc == 5) {
        /* Write Pci */
        bus   = strtoul(argv[0], NULL, 16);
        dev   = strtoul(argv[1], NULL, 16);
        fnc   = strtoul(argv[2], NULL, 16);
        reg   = strtoul(argv[3], NULL, 16);
        wdata = strtoul(argv[4], NULL, 16);
        if (width == 1) {
            pciWrite8(bus, dev, fnc, reg, wdata);
        } else if(width == 2) {
            pciWrite16(bus, dev, fnc, reg, wdata);
        } else if(width == 4) {
            pciWrite32(bus, dev, fnc, reg, wdata);
        } else {
            goto error;
        }
    } else {
        goto error;
    }
    return 0;

error:
    printf("Usage: pci [-w WIDTH] BUS DEVICE FUNCTION REGISTER [DATA]\n"
            "Pci configuration space read or write.\n"
            "  -w        number of byte width. permit 1(default), 2, 4\n"
            "\n"
            "This command executable only root user.\n"
            "\n"
            "Examples:\n"
            "  pci 00 1f 00 00          Read pci from bus 0x00 dev 0x1f func 0x00 reg 0x00.\n"
            "  pci -w2 00 1f 00 10 500  Write pci bus 0x00 dev 0x1f func 0x00 reg 0x10 to 0x500.\n"
            "\n");
    return 1;
}


とりあえず、ばさっと書いただけで、細かく動作確認していなかったり、処理内容もちょっとエラー処理が手抜きぎみだったり、usageがカタコト英語だったりと、つっこみどころはありますが、まあ、個人利用レベルではなんとか使えそうな感じです。

*このブログをみて、プログラムを利用しようとしている方へ
    まったくもって、動作は保証しません。自己責任で使ってください。使用方法はコマンドのUsage参照
     バグに関する報告は歓迎します。それからプログラムの性格上、使いようによっては、
     簡単にシステムを破壊できてしまいます。I/OやPciコンフィグレーション空間について
     知識を有していない方は、使わないほうが懸命です。
     このプログラムを使ってあなたのPCや基板が燃えたり、レンガになっても責任は負いかねます。
   

ビルドはgccが使えるLinux環境で

$ gcc -o io io.c
$ gcc -o pci pci.c

って感じです。あれ、組み込みだとLinuxのファームにコンパイラ入ってなくて別の環境でビルドすることもあると思います。そんときにcライブラリの問題があったりするので、「-static」オプションをつけてビルドすることで、回避できることがあったりなかったりです。


ああ、明日もデバッグもりもりやります。

12/12/2011

TX-50開発キットで遊ぶ その12

熱燗がおいしい季節になりました。最近、なにかと忙しくてあまりTX-50弄る時間が取れなかったんですが、久しぶりに投稿します。シリアルドライバはちょっと行き詰まりぎみなので、保留ぎみで。年末休みとかで、がっつり時間とれるときに腰を据えてやろうかな。

そうそう、久々にIntelのサイトを覗いたらBLDKがアップデートされてました。CrownBay向けのパッケージは現在CB-EDKII-PostGold-2.3.6.7というのが最新です。しかもなんと、Linux向けのパッケージがリリースされてるじゃないですか。素敵です。Linuxだとgccでビルドできるっぽいです。しかし、デバッガとかどうなるんですかねえ、WinDbgベースのIntel UDK Debuggerは使えないでしょうし。今度、試してみます。
http://www.intel.com/p/ja_JP/embedded/hwsw/software/bldk?iid=subhdr-JP+hwsw_bldk#download


というわけで、ソースもいい感じでぐちゃぐちゃになって来たところなので、最新版で仕切りなおしました。動作上目立つところといえば、EfiShellが2.0になってますね。
ああ、あと、今までやったことを適用しても画面映らなかったっす。色々、思考錯誤した結果、CrownBayPlatformPkg.dscの中のgCrownBayPlatformTokenSpaceGuid.PcdGopBltEnable | 0x239A | FALSE というパラメータが原因のようです。これをTRUEにすることでVGAから画面出力でました。

それから、ちょっと開発手法を変えました。以前にVisualStudioでバッチファイルを使ってビルドする方法を紹介しましたが(TX-50開発キットで遊ぶ その3 )、あれ、どうやら問題がありまして、Intel BLDK Development Applicationで変更したパラメータはバッチファイルでビルドしたときに反映されないことが判りました。
どうやらIntel BLDK Development Applicationでパラメータ変更してアプリからビルドすることで、ソースを変更せずに、ビルド時にパラメータを適用しているようです。このパラメータってのは、すなわちCrownBayPlatformPkg.dscに記述されているPCDという仕組みで定義されているパラメータたちです。先述のgCrownBayPlatformTokenSpaceGuid.PcdGopBltEnable もそのひとつです。

ということは逆に、Intel BLDK Development Applicationを使わずに、CrownBayPlatformPkg.dscを直接編集すればバッチファイルからビルドしても設定値が反映されるというわけです。そもそもIntel BLDK Development Applicationは設定項目限られてるし、使いづらいし、TX-50の構成に完全に合致しているわけでもないので、使わないことにしました。

そして、ビルドシステムの仕組みが概ね、解ってきて、CrownBayPlatformPkgというのはそもそも、CrownBay特有のドライバ等のソースの集合体でして、汎用ドライバとかの、それ以外のモジュールはその他のパッケージからもってきて、ファームウェアを構成しています。それをコントロールしているのがCrownBayPlatformPkg.dscというわけです。で、BLDKはEDKII(http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=EDK2)がベースとなっているんですが、EDKIIのソースを見ると、たとえば、ArmPkgや、Omap35xxPkg等、プラットフォームを構成するパッケージが見受けられます。で、ビルドしたいパッケージの.dscファイルを指定することで、ベースパッケージとか汎用ドライバを共有しつつ、そのプラットフォームのファームウェアや、モジュール単体をビルドできるようになってます。

つまり、今回Tx-50というプラットフォームへの移植を行うにあったって、Tx50Pkgというパッケージを作って、そんで、Tx50Pkg.dscを作るという手があるわけです。そうすることで、CrownBayPlatformPkg内のソースを一切変更することなく、必要なモジュールだけTx50Pkg.dscに記述してもってくればスマートです。そしてTx-50特有の部分だけパッケージ内にコピーして編集するなり、新規作成するなりして、Tx50Pkg.dscでそのモジュールを指定すればコントロールもしやすいし、完全に他のパッケージと分離された形で開発できて、いい感じなのです。ただし、この方法を使うとIntel BLDK Development ApplicationはCrownBayPlatformPkg内でしかビルドできないっぽいので、使おうと思っても完全に使えなくなります。まあ、未練はあんまりないんだけどね。あと、Tx50PkgのみをBldkに追加するだけでよくなるので、再配布もし易くなりますね。(需要があるかは解りませんが・・)

ちゅうことで、現在は、ソースのトップレベルにTx50Pkgディレクトリを作ってCrownBayPlatformPkgから.dec、.dsc、.fdfファイルをコピーしてTx50向けに直接編集しちゃいました。そんでカスタムiegd.efiを格納したGopBinaryモジュールと現在コーディング中のPciSerialDxeを追加して、ビルド用のバッチファイルをTx50Pkgをビルドするように編集しちゃって、VisualStudioでプロジェクト作って、VisualStuidoからビルドするという開発スタイルにしました。うーん。なかなか快適です。

C:\BLDKTX50\TX50PKG
│  features.txt
│  Tx50Pkg.dec
│  Tx50Pkg.dsc
│  Tx50Pkg.fdf

├─GopBinary
│      iegd.efi

└─PciSerialDxe
        ComponentName.c
        PciSerialDxe.inf
        Serial.c
        Serial.h


ああ、今日は文章ばっかりになっちゃいました。よし、とりあえず、シリアルドライバはほったらかして、来週は、ぼちぼちOS起動に挑戦しようかなー。

11/14/2011

TX-50開発キットで遊ぶ その11

うーん。シリアル手ごわいっす。今日もちょっと弄ってみたが、進捗は芳しくないです。ICEでもあればいいんですが、シリアルドライバのデバッグにシリアル文字出しを使うという矛盾。ふんにゃかです。
でもまあ初期化はぼちぼち出来てそうです。多分気づいてみれば些細なミスなんじゃないかと気楽な感じでもうちょっとがんばります。

とはいえ、なんにも進捗が無いのは切ないので、気晴らしも兼ねて、EMGDのスプラッシュスクリーンを試してみました。(PCの電源を入れて真っ先にメーカーのロゴとかが出るあれです。)

これはとても簡単でした。EMGDのコンフィグレーションをちょっと弄るだけです。(TX-50開発キットで遊ぶ その7 参照)


まずはConfiturationのほうを弄ります。3ページ目のSplash Screenにチェックを入れます。そうすると左下のグレーアウトされていたところが設定できるようになるので、設定値を入れます。どうやらバックグラウンドカラーのRGBの割合とスプラッシュスクリーンの画像座標が設定できるっぽいですが、今回はお試しなのでオール0でやってみます。その他の設定は以前のままです。




そして、このConfigを使用してパッケージを作ります。パッケージ作成画面の1ページ目でAdd Splash Screenにチェックを入れ、実際の画像を選択します。画像形式は.bmpと.pngが使用できるようです。設定はこれだけです。



今回はペイントで適当に書いた以下の絵を.pngで保存して使ってみます。↓



そして、パッケージをビルドして出来たiegd.efiをbldkに突っ込んでビルドしてみました。んで起動です。


みごとスプラッシュスクリーン出てくれました。もうちょっとかっこよくしてみたいですねー。


今日はここまでです。来週こそはシリアルコンソール出すですよ。

11/07/2011

TX-50開発キットで遊ぶ その10

本日はシリアルドライバを作り始めます。ターミナルでシリアルコンソールを動かす目論みです。

実はBLDKには元々レガシーのシリアルドライバが含まれています。(IntelFrameworkModulePkg\Bus\Isa\IsaSerialDxe)

SIOとかのシリアルならこれで動くのですが、残念ながらPCIシリアルだと動かないのです。Tx-50にはSIOが乗ってなくてPCIシリアルしかないので、このIsaSerialDxeドライバを元にPCIシリアルドライバを作る方向でいっちょやってみます。

というわけで、おもむろにIsaSerialDxeをディレクトリごとCrownBayPlatformPkgディレクトリにPciSerialDxeという名前でコピーしてしまいます。そしてファイル中のIsaSerialと名のつくものは片っ端からPciSerialに置き換えまして、EFI_ISA_IO_PROTOCOLというプロトコルを使ってるやつもEFI_PCI_IO_PROTOCOLに置き換えます。

そして、SerialControllerDriverSupported()関数はこのままじゃちょっとまずそうです。他のPCI系のドライバソースを参考に、HIDの0x501のチェックをコメントアウトして、PCIコンフィグレーション空間のクラスコードとサブクラスコードが16550互換のシリアルデバイスかどうかチェックする処理を加えました。あと、このままだと、Tx-50の4つのPCIシリアルポートが4つともドライバ当たってしまって話がややこしくなるので、PCIのベンダーIDとデバイスIDもチェックしてRS232Cのコネクタがついてるポート(bus:2/dev:0a/func:1)だけにドライバが当たるように処理を追加しました。と、あとコマンドレジスタのI/O Space Enableもこの段階で有効にしてしまうことにしました。

ほんで、SerialControllerDriverStart()関数もちょっといじる必要がありそうです。BaseAddrssを取得するところをACPIのリソースリストから取得しているところを、PCIコンフィグレーション空間のベースアドレスレジスタから取得するように変更です。
で、CrownBayPlatformPkg.dscとCrownBayPlatformPkg.fdfの適当なところにCrownBayPlatformPkg/PciSerialDxe/PciSerialDxe.inf と書いとけば、ビルドされます。ビルドエラーがちょこまかと出ますが、姑息なつじつま合わせを駆使しつつ、どうにかビルドを通しました。

うーん。こんな力づくでいいんだろうか。まあスタート地点としては良しとしよう。こまいところは今後仕上げていくとして、動かす目処を立てることを優先します。

で、電源入れてみたけど、まあ動きません。とはいえ、ちまちま思考錯誤を繰り返し、一応ドライバが当たるところまでは行きました。

EfiShellのdevicesコマンドとdriversコマンドの出力です。

Shell> drivers

            T   D
D           Y C I
R           P F A
V  VERSION  E G G #D #C DRIVER NAME                         IMAGE NAME
== ======== = = = == == =================================== ===================
5C 0000000A B - -  1 35 PCI Bus Driver                      PciBusDxe
5D 0000000A B - -  1  1 PCI Serial Driver                   PciSerialDxe

5E 00000030 D - -  2  - Usb Ehci Driver                     EhciDxe
5F 00000010 D - -  6  - Usb Ohci Driver                     OhciDxe
60 0000000A B - -  8  3 Usb Bus Driver                      UsbBusDxe
61 0000000A D - -  1  - Usb Keyboard Driver                 UsbKbDxe
62 0000000A D - -  1  - Usb Mouse Driver                    UsbMouseDxe
63 00000011 D - -  1  - Usb Mass Storage Driver             UsbMassStorageDxe
64 00000020 D - -  2  - EFI SD Host Controller Driver       SDController
65 00000020 D - -  2  - UEFI MMC/SD Media Device Driver     SDMediaDevice
66 0000000A D - -  1  - Generic Disk I/O Driver             DiskIoDxe
67 0000000A ? - -  -  - Partition Driver(MBR/GPT/El Torito) PartitionDxe
68 00000010 D - -  1  - SATA Controller Init Driver         AhciDxe
69 0000000A D - -  1  - AHCI Bus Driver                     AhciDxe
6A 00000001 ? - -  -  - <UNKNOWN>                           Lpc47m17x
6B 0000000A B - -  1  3 ISA Bus Driver                      IsaBusDxe
6C 0000000A ? - -  -  - ISA Serial Driver                   IsaSerialDxe
6D 0000000A ? - -  -  - PS/2 Keyboard Driver                Ps2KeyboardDxe
6E 0000000A ? - -  -  - PS/2 Mouse Driver                   Ps2MouseDxe
6F 0000000A D - -  1  - Platform Console Management Driver  ConPlatformDxe
70 0000000A D - -  1  - Platform Console Management Driver  ConPlatformDxe
71 0000000A B - -  1  1 Console Splitter Driver             ConSplitterDxe
72 0000000A B - -  1  1 Console Splitter Driver             ConSplitterDxe
73 0000000A ? - -  -  - Console Splitter Driver             ConSplitterDxe
74 0000000A B - -  1  1 Console Splitter Driver             ConSplitterDxe
75 0000000A ? - -  -  - Console Splitter Driver             ConSplitterDxe
78 0000000A D - -  1  - UGA Console Driver                  GraphicsConsoleDxe
79 0000000A B - -  1  1 Serial Terminal Driver              TerminalDxe
7C 01082032 B X -  1  1 IEGD GOP Driver                     FvFile(FF0C8745-32
7D 0000000A D - -  1  - FAT File System Driver              FvFile(961578FE-B6



Shell> devices

C  T   D
T  Y C I
R  P F A
L  E G G #P #D #C Device Name
== = = = == == == =============================================================
47 R - -  -  1 35 PciRoot(0x0)
76 D - -  2  -  - Primary Console Input Device
77 D - -  1  -  - Primary Console Output Device
81 D - -  1  -  - PciRoot(0x0)/Pci(0x0,0x0)
82 D - -  1  -  - PciRoot(0x0)/Pci(0x1,0x0)
83 B X -  1  1  1 IEGD Graphics Controller
84 D - -  1  -  - PciRoot(0x0)/Pci(0x3,0x0)
85 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)
86 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)
87 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0x0,0x0)
88 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0x0,0x1)
89 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0x0,0x2)
8A D - -  1  2  - Usb Universal Host Controller
8B D - -  1  2  - Usb Universal Host Controller
8C D - -  1  2  - Usb Universal Host Controller
8D B - -  1  2  1 Enhanced Host Controller (USB 2.0)
8E D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0x2,0x4)
8F D - -  1  2  - SD Host Controller
90 D - -  1  2  - SD Host Controller
91 D - -  1  2  - IOH Serial ATA Controller
92 D - -  1  2  - Usb Universal Host Controller
93 B - -  1  2  2 Usb Universal Host Controller
94 D - -  1  2  - Usb Universal Host Controller
95 D - -  1  2  - Enhanced Host Controller (USB 2.0)
96 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x0)
97 B - -  1  1  1 PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x1)
98 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x2)
99 D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x3)
9A D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x4)
9B D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xC,0x0)
9C D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xC,0x1)
9D D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xC,0x2)
9E D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xC,0x3)
9F D - -  1  -  - PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xC,0x4)
A0 D - -  1  -  - PciRoot(0x0)/Pci(0x18,0x0)
A1 D - -  1  -  - PciRoot(0x0)/Pci(0x18,0x0)/Pci(0x0,0x0)
A2 D - -  1  -  - PciRoot(0x0)/Pci(0x1B,0x0)
A3 B - -  1  1  3 PciRoot(0x0)/Pci(0x1F,0x0)
A4 B X -  1  3  1 IEGD Graphics Controller Display Device #0
A5 D - -  1  -  - PciRoot(0x0)/Pci(0x1F,0x0)/Serial(0x0)
A6 D - -  1  -  - PciRoot(0x0)/Pci(0x1F,0x0)/Acpi(PNP0303,0x0)
A7 D - -  1  -  - PciRoot(0x0)/Pci(0x1F,0x0)/Acpi(PNP0F03,0x0)
A9 D - -  1  3  - FAT File System
AA B - -  1  1  1 PciRoot(0x0)/Pci(0x17,0x0)/Pci(0x0,0x0)/Pci(0xA,0x1)/Uart

(115200,8,N,1)
AB D - -  1  -  - PC-ANSI Serial ConsoleAC B - -  1  3  1 Generic Usb Keyboard
AD B - -  1  2  1 Generic Usb Mouse


ちなみに、TerminalDxeというやつがシリアルコンソールのドライバでBLDKに最初から含まれています。なのでシリアルドライバがうまく動けばシリアルコンソールも動く手筈です。ドライバはちゃんと当たってPC-ANSI Serial Consoleも正常に認識してそうです。

ターミナルエミュレータはTeraTermを使ってますが、起動中に一瞬反応して画面クリアかなんかが入ります。細かいところを見て直していけば、もうちょっとで動きそうな予感です。

今日はもう力尽きたので、また来週がんばります。ソースは綺麗に書き直したら載せる(かも)。

10/31/2011

TX-50開発キットで遊ぶ その9

今日は、シリアルのドライバを作成するに先立って、ちょっとお勉強です。

まず、BLDKはEDKIIがベースになってます。以前に弄ってたEDKとはモジュールの扱いの流儀がちょっと違うようで、まだよくわかってません。なので、EDKIIのモジュール作成方法について調べてみます。

EDKIIのドキュメントは以下からゲットします。

http://sourceforge.net/projects/edk2/files/General%20Documentation/
http://sourceforge.net/projects/edk2/files/Specifications/

なんだか読み物がたくさんありますが、さしあたって、EDKII Module Writer_s Guide_0_7.pdfをざっくり眺めてみました。

まだ、完全に内容を理解できてないですが、まずドライバやアプリのモジュールを作成するにはxxxxPkgとかって名前でディレクトリを作って、複数のモジュールをまとめておくパッケージを作成することになってるようです。

んで、そのパッケージにはDECとDSCファイルというパッケージの定義やビルドの挙動を記述したファイルを作成することになってるっぽいです。あと、FDFファイルというのもあって、これは、フラッシュ(ROMイメージ)に何を含めるかということを記述するようです。これは全てのモジュールに必要ではなくて、最終成果物としてROMイメージを出力したいモジュールだけ必要になるようです。

そして、パッケージの中にドライバ等のモジュールを(必要なだけ複数でも)作成することになってて、DXEドライバならxxxxDxeとかっていうフォルダにするのがお勧めみたいです。このフォルダにソースコードやバイナリモジュールならバイナリを置くわけですが、それぞれのモジュールにINFファイルというのを作成する必要があるようです。


なんだか、DECやらDSCとかFDFとかINFとか複雑なことになってますが、ビルド時にこれらのメタデータを元にmakefileが自動生成されるようです。EDKIIでは以前のEDKと違い、ビルド環境が、クロスプラットフォームで、複数のコンパイラやビルドシステム、OSに対応しているため、こんなことになってるっぽいです。

あと、PCDなる機構があって、これをうまくつかうことで、メタデータから定数や機能のON/OFFをコントロールできる仕組みが用意されているようです。

で、これらメタデータの記述ルールに関しては別途Specificationのドキュメントが個々に用意されているのですが、まだ全部目を通せていません。

とはいえ、ドキュメント読んでばかりだと、つまらないので、とにかくなんか作ってみてみます。そのほうが理解も早いしね。

そんなわけで、今回は特に何もしてくれないテストドライバを適当に書いてみます。目的はどっちかというとビルドシステムに慣れることですね。とりあえず、BLDKにはいくつもドライバソースが含まれているので、それらを参考にします。8254TimerDxeあたりがシンプルな構造で参考になりました。

で、ごにょごにょ試行錯誤しつつ以下のコードを書いてみました。


//
// TestDriver.c
//

#include "TestDriver.h"

EFI_GUID gEfiTestDriverProtocolGuid = EFI_TEST_DRIVER_PROTOCOL_GUID;

EFI_HANDLE                mTestHandle = NULL;

//
// driver produces
//
EFI_TEST_DRIVER_PROTOCOL   mTest = {
  TestDriverFunction
};

EFI_STATUS
EFIAPI
TestDriverFunction (
)
{
  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
TestDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Install the Test Driver Protocol onto a new handle
  //
  return gBS->InstallMultipleProtocolInterfaces (
                  &mTestHandle,
                  &gEfiTestDriverProtocolGuid,
                  &mTest,
                  NULL
                  );
}




//
// TestDriver.h
//

#ifndef __TEST_DRIVER_H__
#define __TEST_DRIVER_H__

#include <Library/UefiBootServicesTableLib.h>
#include "TestDriverProtocol.h"

EFI_STATUS
EFIAPI
TestDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

EFI_STATUS
EFIAPI
TestDriverFunction (
  );

#endif



//
// TestDriverProtocol.h
//

#ifndef __TEST_DRIVER_PROTOCOL_H__
#define __TEST_DRIVER_PROTOCOL_H__

#define EFI_TEST_DRIVER_PROTOCOL_GUID \
  { 0x8d37d58, 0x8c26, 0x4e39, { 0x92, 0xf3, 0x14, 0x97, 0x89, 0x5f, 0x60, 0x63 } }

typedef 
EFI_STATUS
(EFIAPI *EFI_TEST_DRIVER_FUNCTION)(
  );

typedef struct _EFI_TEST_DRIVER_PROTOCOL {
  EFI_TEST_DRIVER_FUNCTION          TestFunction;
}EFI_TEST_DRIVER_PROTOCOL;

extern EFI_GUID gEfiTestDriverProtocolGuid;

#endif





#
# TestDriver.inf
#

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = TestDriver
  FILE_GUID                      = CD5E357E-94D9-484d-BA43-CB632FC50C03
  MODULE_TYPE                    = DXE_DRIVER
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = TestDriverEntryPoint

[Packages]
  MdePkg/MdePkg.dec
  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec

[LibraryClasses]
  UefiDriverEntryPoint
  UefiBootServicesTableLib
  UefiLib

[Sources]
  TestDriverProtocol.h
  TestDriver.h
  TestDriver.c

[Protocols]

[Depex]
  TRUE





で、DECやDSCも必要かと思いきや、どうやらパッケージ単体でビルドする場合は必要っぽいですが、この場合BLDKのROMイメージを作成するので、ビルド対象がCrownBayPlatformPkgになります。なので、このパッケージのDSCとFDFの適当な所にTestDriver.infへのパスを書いておけばビルドされるぽいです。ほんとはルールに則ってパッケージ毎にあったほうがいいんでしょうけどね。

で、ビルドは通ったものの、何もしないドライバなので、動かしても何も起きないです。ふんにゃかです。

でもまあ、パッケージ作成の理解は進んだし、概ね雰囲気も掴めてきたので、良しとします。来週はシリアルドライバ作成に着手したいところです。

週一の開発ペースだと、ボリュームの大きめな課題は中々進まないですねえ。ちまちまがんばります。


今週はここまでっす。

10/24/2011

TX-50開発キットで遊ぶ その8

そして、シリアルポートについて、考えます。

シリアルポートが使えるようにしたい理由は2つあって、一つは、シリアルコンソールを使えると、便利なのと、もう一つは、BLDKのドキュメントによれば、シリアルを使ってソースレベルデバッグができるっぽいのです。

で、BLDKには、この二つをサポートするためのコードが含まれているのですが、これが、残念ながら、SuperIOのレガシーシリアルポート向けのコードで、TX-50では使えません。しかしながらPCIシリアルでなんとかならんもんかと、考えてみます。

シリアルコンソールに関しては、恐らく、PCIシリアルのDXEドライバを書けば使える可能性が高いんじゃなかろうかと思います。とはいえ、ドライバ書くのは結構時間かかりそうですねえ。

デバッガに関しては、恐らく、BIOSのかなり前のほうでシリアルが初期化されてないと駄目っぽいですね。うーん。どちらにしても、とりあえず、TX-50のPCIバストポロジをもう少し、詳しく把握しておく必要がありそうです。というわけで、EFIShellのPCIコマンドでデバイス一覧をみてみます。

Shell>pci



   Seg  Bus  Dev  Func
   ---  ---  ---  ----
    00   00   00    00 ==> Bridge Device - Host/PCI bridge
             Vendor 8086 Device 4114 Prog Interface 0
    00   00   01    00 ==> Bridge Device - Host/PCI bridge
             Vendor 8086 Device 8183 Prog Interface 0
    00   00   02    00 ==> Display Controller - VGA/8514 controller
             Vendor 8086 Device 4108 Prog Interface 0
    00   00   03    00 ==> Multimedia Device - Video device
             Vendor 8086 Device 8182 Prog Interface 0
    00   00   17    00 ==> Bridge Device - PCI/PCI bridge
             Vendor 8086 Device 8184 Prog Interface 0
    00   00   18    00 ==> Bridge Device - PCI/PCI bridge
             Vendor 8086 Device 8185 Prog Interface 0
    00   00   1B    00 ==> Multimedia Device - UNDEFINED
             Vendor 8086 Device 811B Prog Interface 0
    00   00   1F    00 ==> Bridge Device - PCI/ISA bridge
             Vendor 8086 Device 8186 Prog Interface 0
    00   01   00    00 ==> Bridge Device - PCI/PCI bridge
             Vendor 8086 Device 8800 Prog Interface 0
    00   02   00    00 ==> Device does not fit in any defined classes - 
             Vendor 8086 Device 8801 Prog Interface 0
    00   02   00    01 ==> Network Controller - Ethernet controller
             Vendor 8086 Device 8802 Prog Interface 0
    00   02   00    02 ==> Device does not fit in any defined classes - 
             Vendor 8086 Device 8803 Prog Interface 0
    00   02   02    00 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 8804 Prog Interface 10
    00   02   02    01 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 8805 Prog Interface 10
    00   02   02    02 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 8806 Prog Interface 10
    00   02   02    03 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 8807 Prog Interface 20
    00   02   02    04 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 8808 Prog Interface FE
    00   02   04    00 ==> Base System Peripherals - UNDEFINED
             Vendor 8086 Device 8809 Prog Interface 1
    00   02   04    01 ==> Base System Peripherals - UNDEFINED
             Vendor 8086 Device 880A Prog Interface 1
    00   02   06    00 ==> Mass Storage Controller - UNDEFINED
             Vendor 8086 Device 880B Prog Interface 1
    00   02   08    00 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 880C Prog Interface 10
    00   02   08    01 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 880D Prog Interface 10
    00   02   08    02 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 880E Prog Interface 10
    00   02   08    03 ==> Serial Bus Controllers - USB
             Vendor 8086 Device 880F Prog Interface 20
    00   02   0A    00 ==> Device does not fit in any defined classes - 
             Vendor 8086 Device 8810 Prog Interface 0
    00   02   0A    01 ==> Simple Communications Controllers - Serial controller
             Vendor 8086 Device 8811 Prog Interface 2
    00   02   0A    02 ==> Simple Communications Controllers - Serial controller
             Vendor 8086 Device 8812 Prog Interface 2
    00   02   0A    03 ==> Simple Communications Controllers - Serial controller
             Vendor 8086 Device 8813 Prog Interface 2
    00   02   0A    04 ==> Simple Communications Controllers - Serial controller
             Vendor 8086 Device 8814 Prog Interface 2
    00   02   0C    00 ==> Device does not fit in any defined classes - 
             Vendor 8086 Device 8815 Prog Interface 0
    00   02   0C    01 ==> Serial Bus Controllers - Other bus type
             Vendor 8086 Device 8816 Prog Interface 0
    00   02   0C    02 ==> Serial Bus Controllers - Other bus type
             Vendor 8086 Device 8817 Prog Interface 0
    00   02   0C    03 ==> Serial Bus Controllers - UNDEFINED
             Vendor 8086 Device 8818 Prog Interface 0
    00   02   0C    04 ==> Device does not fit in any defined classes - 
             Vendor 8086 Device 8819 Prog Interface 0
    00   03   00    00 ==> Network Controller - Ethernet controller
             Vendor 8086 Device 10D3 Prog Interface 0


なにやら、デバイスてんこ盛りですな。これだと、ちょっとイメージ沸きづらいので、図にしてみました。デバイス名はデータシートと照らし合わせて調べました。

TX-50 BLDK Bus Topology

六角形のはブリッジです。データシートによればATOM側にはPCI Expressのルートポート(PCIバス上ではブリッジに見える)が4つあるはずですが、2つしか見えないので、残り2つは無効にされているっぽいです。(点線のやつ)

そして、問題のシリアルはBus 2/Device 0Ah/Function 1ですね。このデバイスとHostブリッジの間にはPCI Expressのブリッジが2つ入ることがわかりました。(ピンクのやつ)それは、すなわち、このデバイスにアクセスするには2つのブリッジのI/O(またはメモリ)ウィンドウを設定する必要があるということです。
これらのブリッジがサブトラクティブデコードに対応していれば、もう少し話しは簡単だったかもしれませんが、残念ながら、データシートにそのような記述はみあたりませんでした。

しかも、PCI Expressブリッジ2つをBIOSの頭で初期化するにしても、PCI Expressにはリンクトレーニングというものがありまして、いきなり使えるとは限らないですね。そもそも、チップセットの特殊な領域を設定しないとリンクトレーニングが始まらないという可能性もあるため、うまくいくとも限りませんし、うまくいってないときに何が起きているのかも知る術がないですねー。

ま、試しに、やってみっか。

今週はここまでにしときます。来週またがんばるっすー。

TX-50開発キットで遊ぶ 開発ツール編

今週から新しい開発ツールの仲間が増えました。

ROMライターのEZP2010さんです。

EZP2010


これで、動作中にホットスワップという危険な行為をせずとも、BIOS焼けます。今までだと、元のBIOSをうっかり壊して、起動するBIOSが一個も無くなってしまったら、打つ手無しという危険もありましたが、これで、心おきなく焼きまくりです。

このROMライター、ぐぐってて、たまたま、デジダイブさんという方のブログで、このROMライターについて取り上げておられたのを見つけて、私も買うことにしました。ヤフーチャイナモールで3000円程度という、ハードボイルドな価格設定でしたが、デジダイブさんの記事を事前に読ませて頂いたので、思い切って買うことができました。デジダイブさん、ありがとうございます。
http://www.ddive.net/2011/08/23/ezp2010/


で、これ、見かけは、もはや大人の科学の付録にしかみえませんが、結構、高速に焼けます。ソフトもわりと、必要十分な出来栄えで、対応SPIの量もなかなかのものです。

EZP2010 soft
惜しいのは、ROMイメージを読み込む際に、2MBのSPIに1MBのイメージを後半のアドレスを指定してロードできないので、ちょっと不便です。前もって、ROMイメージを加工すればいいんですけどね。



次に紹介するのは、A390というUSBデータリンクケーブルです。
A390
これ、やべえっす。以前に紹介記事をみつけて、ずっと気になってたんですが、土曜日(10/22)に別件で秋葉原行ってきたので、ついでに探してたら、あきばおーで見つけました。

これ、簡単に言うと、PC上のファイルをマスストレージに見せて、メディアプレイヤーで再生できるという主旨の製品なんですが、ISOもイケます。なので、OSのCDイメージファイルをいちいち、CDに焼いたり、USBメモリに書き込まなくても、ケーブルつないで、ファイル指定するだけで、もう一方のPCはそのイメージでブートできちゃいます。
(早速これを使って、ネットブックにUbuntu11.10をインストールしてみましたが、問題なくできました。)

あらゆる面でエコです。すばらしいです。そして、当然ファイルもいけるので、EFIShellやBIOSイメージや、SpiUpdate.efi等を突っ込んだ、バーチャルディスクを作れば、それで、起動してBIOS書き込んだりできます。もう、これは開発ツールとしても、すばらしいです。

ちょっと残念なのは、書き込みができないようです。書き込みできたら最強なのになー。


ツールの紹介は以上です。