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」オプションをつけてビルドすることで、回避できることがあったりなかったりです。


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

0 件のコメント:

コメントを投稿