Upgrade to Pro — share decks privately, control downloads, hide ads and more …

デバイスドライバを作ってみよう!

 デバイスドライバを作ってみよう!

457c3c757b4fae74c7cdc79ad67a5645?s=128

Masami Ichikawa

May 08, 2010
Tweet

Transcript

  1. 第 4 回 カーネル/ VM 探検隊 2010/05/08 @masami256 デバイスドライバを作ってみよう!

  2. 何でデバドラを作るの? • 愛用している OS にドライバがな い • 自作 OS でデバイスを使いたい

  3. ドライバ作成の準備 • 資料を揃える – メジャーなデバイスだと、 TECH I が便利 • ドライバを書きたい

    OS の作法を調べる – Linux, OpenBSD, Plan 9 and so forth • ユーザランドで実装できる場合もある – USB デバイスなら libusb が使える • 実装方法を考える
  4. ATA デバイスの認識フロー概要

  5. PCI デバイスの検索 • 下記 3 個の組み合わせを総当たりで – デバイス番号 0 〜

    7 – バス番号 0 〜 31 – 機能番号 0 〜 7 • 機能番号が 0 のデバイスの場合の注意 – コンフィギュレーションレジスタからヘッダタ イプを読み込む – マルチファンクションか調べる – マルチファンクションで無ければ、機能番号 1 〜 7 にはデバイスはなし
  6. PCI デバイスの検索 • Config Address レジスタ( 0x0cf8 )に対して、どのデバ イス・バス・機能番号を使うかを書き込む –

    bit0-1 : 0 – bit2-7 :レジスタアドレス – bit8-10 :機能番号 – bit11-15 :デバイス番号 – bit16-23 :バス番号 – bit24-30 : 0 – bit31 :データを読み書きするときは 1 に • レジスタアドレスに、読み出したいデータのアドレスを セット
  7. PCI デバイスの検索 • Configuration レジスタからデータを読む – レジスタの範囲は 0x0cfc 〜 0x0cff

    – I/O ポートに 0x0cfc を指定して 32bit アクセスすれば OK • 主なデータ – 16 進数は Config Address で指定するレジスタアドレス – ベンダ ID : 0x00 : bit0-15 – デバイス ID : 0x00:bit16-31 – クラスコード: 0x08 : bit8-31 – ヘッダタイプ :0x0c:bit16-23 • bit23 :マルチファンクションデバイス
  8. PCI でデバイス検索

  9. HDD の初期化 initialize_ata() |--> initialize_common() | |--> get_device_type() | |

    |--> do_identify_device() | | | |--> do_device_selection_protocol() | | | | |--> wait_until_BSY_and_DRQ_are_zero() | | | |--> get_DRDY() | | | |--> write_command() コマンド 0xec の Write | | | |--> wait_until_BSY_is_zero() | | | |--> inb() Alternate Status レジスタの Read | | | |--> inb() Status レジスタの Read | | | |--> is_error() | | | |--> is_drq_active() | | | |--> is_device_fault() | | | |--> inw() データレジスタの Read
  10. HDD の R/W 共通部前半 static bool sector_rw_common(u_int8_t cmd, int device,

    u_int32_t sector) { ... // nIEN bit should be enable and other bits are disable. outb(DEVICE_CONTROL_REGISTER, 0x02); // Features register should be 0. outb(FEATURES_REGISTER, 0x00); // Set Logical Sector. outb(SECTOR_NUMBER_REGISTER, sector & 0xff); outb(CYLINDER_LOW_REGISTER, (sector >> 8) & 0xff); outb(CYLINDER_HIGH_REGISTER, (sector >> 16) & 0xff); outb(DEVICE_HEAD_REGISTER, ((sector >> 24) & 0x1f) | 0x40); outb(SECTOR_COUNT_REGISTER, 1); // Execute command. outb(COMMAND_REGISTER, cmd); Read:0x20 Write:0x30 LBA 方式で アドレスを設定
  11. HDD の R/W 共通部後半 DRQ ビットがアクティブに なるまで実行 ちょっと待ってから、 データの空読み wait_loop_usec(4);

    inb(ALTERNATE_STATUS_REGISTER); read_status_register_again: status = inb(STATUS_REGISTER); if (is_error(status)) { ... } if (!is_drq_active(status)) { if (loop > 5) { ... } loop++; goto read_status_register_again; } return true; }
  12. HDD の Read 処理 int read_sector(int device, u_int32_t sector, sector_t

    *buf, size_t buf_size) { ... ret = sector_rw_common(PIO_SECTOR_READ_CMD, device, sector); if (!ret) return -1; for (i = 0; i < buf_size; i++) buf[i] = inw(DATA_REGISTER); finish_sector_rw(); return 0; } static inline void finish_sector_rw(void) { inb(ALTERNATE_STATUS_REGISTER); inb(STATUS_REGISTER); } 読み込みが終わったら 終了処理
  13. HDD からデータを読む 00001580 a4 81 01 00 e8 03 e8

    03 07 00 00 00 39 11 db 4b |............9..K| 00001590 39 11 db 4b 39 11 db 4b dd 00 00 00 00 00 00 00 |9..K9..K........| 000015a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00036400 03 00 2e 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00036410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00036420 02 00 2e 2e 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00036430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00036440 07 00 66 6f 6f 62 61 72 2e 74 78 74 00 00 00 00 |..foobar.txt....| 00036450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00037400 66 6f 6f 62 61 72 0a 00 00 00 00 00 00 00 00 00 |foobar..........| 00037410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * mkfs.minix (バージョンは V2 )で作った HDD のイメージで、 /dir_a/dir_b/foobar.txt を読みます。 ダンプの 00001580 が foobar.txt の inode で、 00036440 から始まっているのはディレクトリエントリ、 00037400 から始まっているのが実データです。
  14. HDD からデータを読む

  15. libusb + YUREX int Yurex::findDevices() throw (const char *) {

    int cnt = 0; libusb_device **devs; cnt = libusb_get_device_list(NULL, &devs); if (cnt < 0)   throw "There are no USB devices"; setDevices(devs); return cnt; } 最初に USB デバイスを探します
  16. libusb + YUREX bool Yurex::checkYurexDevice() throw (const char *) {

    libusb_device *dev; libusb_device **devs = getDevices(); int i = 0; bool ret = false; while ((dev = devs[i++]) != NULL) { struct libusb_device_descriptor desc; int r = libusb_get_device_descriptor(dev, &desc); if (r < 0) throw ("failed to get device descriptor"); if (desc.idVendor == YUREX_VENDOR_ID && desc.idProduct == YUREX_PRODUCT_ID) { setDescriptor(desc); setDevice(dev); ret = true; break; } } return ret; } 見つかったデバイスから、 YUREX があるかをチェック
  17. libusb + YUREX bool Yurex::openYurex() { libusb_device_handle *h; h =

    libusb_open_device_with_vid_pid(NULL, getDescriptor()->idVendor, getDescriptor()->idProduct); if (h)     setHandle(h); clearDeviceList(); return h ? true : false; } YUREX が見つかったら、 デバイスをオープン
  18. libusb + YUREX bool Yurex::claimToYurex() { libusb_device_handle *handle = getHandle();

    int ret; ret = libusb_claim_interface(handle, 0); if(ret < 0) std::cout << "Cannot Claim Interface" << std::endl; return ret < 0 ? false : true; } YUREX を使えるようにしていきます
  19. libusb + YUREX void Yurex::findEndPoint() { libusb_config_descriptor *config = getConfig();

    const libusb_interface_descriptor *interdesc; const libusb_endpoint_descriptor *epdesc; const libusb_interface *inter; libusb_get_config_descriptor(getDevice(), 0, &config); // Actually, yulex may only have one endpoint. for(int i = 0; i < (int) config->bNumInterfaces; i++) { inter = &config->interface[i]; for(int j = 0; j < inter->num_altsetting; j++) { interdesc = &inter->altsetting[j]; for(int k = 0; k < (int) interdesc->bNumEndpoints; k++) { epdesc = &interdesc->endpoint[k]; setEndPoint(epdesc); } } } } YUREX の設定はこれで終わりです
  20. libusb + YUREX bool Yurex::readDataSync() { unsigned char data[8] =

    { CMD_PADDING }; int ret; int actual = 0; data[0] = CMD_READ; data[1] = CMD_EOF; ret = libusb_bulk_transfer(getHandle(), getEndPoint()->bEndpointAddress, data, sizeof(data), &actual, 2000); if(ret < 0) {    std::cout << "Reading Error" << std::endl; } else {    std::cout << "Reading Successful!" << std::endl; for (int i = 0; i < sizeof(data); i++) std::cout << std::hex << std::showbase << (int) data[i] << ":"; std::cout << std::endl; } return true; } YUREX からデータの読み込み
  21. libusb + YUREX [masami@moonlight:~/experiment/yurex]% sudo ./yurex Yurex info Vendor: 0xc45

    Product: 0x1010 Open Yurex device is success Kernel Driver Active Kernel Driver Detached! Claim to Yurex device ret is 0 Start search endpoint find endpoint Number of alternate settings: 0x1 | Interface Number: 0 | Number of endpoints: 0x1 | Descriptor Type: 0x5 | EP Address: 0x81 | ret is 0 : actual is 0x8 Writing Successful! ret is 0 : actual is 0x8 Reading Successful! 0x43:0:0:0:0:0x94:0xd:0: Done.
  22. ご清聴ありがとうございました @yojiro さんのプレゼン資料「 OPENBSD MEETS "YUREX" 」 http://groups.google.com/group/kernelvm ↑ の「第三回

    カーネル/ VM 探検隊まとめ」 資料で使用したソースコード http://github.com/masami256/miko http://github.com/masami256/yurex はてなダイアリー http://d.hatena.ne.jp/masami256/