Go to main content
Oracle® Solaris 11.3 デバイスドライバの記述

印刷ビューの終了

更新: 2016 年 11 月
 
 

一般的な変換段階

以降のセクションでは、64 ビット環境で動作するようにドライバを変換する方法について説明します。ドライバの作成者は、次のタスクの 1 つ以上を実行する必要があることがあります。

  1. 固定幅型をハードウェアレジスタに使用します。

  2. 固定幅の共通アクセス関数を使用します。

  3. 派生型の使用を確認および拡張します。

  4. DDI データ構造体内の変更されたフィールドを確認します。

  5. DDI 関数の変更された引数を確認します。

  6. 必要に応じて、ユーザーデータを処理するドライバエントリポイントを変更します。

  7. x86 プラットフォームで 64 ビットの long 型を使用する構造体を確認します。

これらの手順について、以降で詳しく説明します。

各手順の完了後に、コンパイラの警告をすべて修正し、lint を使用してほかの問題を検索します。64 ビットの問題を検索するときは、–Xarch=v9 および –errchk=longptr64 を指定して、SC5.0 (またはそれ以降の) バージョンの lint を使用してください。


注 - LP64 用の変換時のコンパイル警告を無視しないでください。以前は ILP32 環境で無視しても安全だった警告が、重大な問題を示すようになっていることがあります。

すべての手順を完了したら、32 ビットと 64 ビットの両方のモジュールとしてドライバをコンパイルおよびテストします。

固定幅型をハードウェアレジスタに使用する

ハードウェアデバイスを操作する多くのデバイスドライバは、C データ構造体を使用してハードウェアのレイアウトを記述します。LP64 データモデルでは、long が 64 ビット量になったため、long または unsigned long を使用してハードウェアレジスタを定義するデータ構造体はほぼ確実に正しくありません。最初に <sys/inttypes.h> をインクルードして、32 ビットのデバイスデータとして long の代わりに int32_t または uint32_t を使用するように、このクラスのデータ構造体を更新します。この方法により、32 ビットデータ構造体のバイナリレイアウトが保持されます。次に変更前の例を示します。

struct device_regs {
    ulong_t        addr;
    uint_t         count;
};      /* Only works for ILP32 compilation */

これを次のように変更します。

struct device_regs {
    uint32_t        addr;
    uint32_t        count;
};      /* Works for any data model */

固定幅の共通アクセス関数を使用する

Oracle Solaris DDI では、アクセス関数でデバイスレジスタにアクセスすることにより、複数のプラットフォームでの移植性を確保できます。以前は、DDI 共通アクセス関数はバイトやワードなどでデータのサイズを指定しました。たとえば、32 ビット量にアクセスするには、ddi_getl(9F) を使用します。この関数は 64 ビットの DDI 環境では使用できず、動作するビット数を指定するバージョンの関数に置き換えられています。

これらのルーチンは、ドライバの作成者が早期に採用できるように、Solaris 2.6 オペレーティング環境の 32 ビットカーネルに追加されました。たとえば、32 ビットと 64 ビットの両方のカーネルに移植できるようにするには、ドライバは ddi_getl(9F) ではなく ddi_get32(9F) を使用して 32 ビットのデータにアクセスする必要があります。

共通アクセスルーチンはすべて、同等の固定幅のルーチンに置き換えられています。詳細は、ddi_get8(9F)、ddi_put8(9F)、ddi_rep_get8(9F)、および ddi_rep_put8(9F) のマニュアルページを参照してください。

派生型の使用を確認および拡張する

可能な場合は size_t などのシステム派生型を使用して、結果として得られる変数が関数間で渡されるときに意味をなすようにしてください。新しい派生型である uintptr_t または intptr_t は、ポインタの整数型として使用してください。

固定幅の整数型は、バイナリデータ構造体またはハードウェアレジスタのサイズを明示的に表すのに便利です。一方、int などの C 言語の基本データ型は、ループカウンタまたはファイル記述子で引き続き使用できます。

一部のシステム派生型は、32 ビットシステムでは 32 ビット量を表しますが、64 ビットシステムでは 64 ビット量を表します。この方法でサイズを変更する派生型には、clock_t、daddr_t、dev_t、ino_t、intptr_t、off_t、size_t、ssize_t、time_t、uintptr_t、および timeout_id_t があります。

これらの派生型を使用するドライバを設計するときは、特にドライバがこれらの値を固定幅型などの別の派生型の変数に割り当てる場合、これらの型の使用に特別な注意を払ってください。

DDI データ構造体内の変更されたフィールドを確認する

buf(9S) など、DDI データ構造体内の一部のフィールドのデータ型は変更されました。これらのデータ構造体を使用するドライバでは、これらのフィールドが適切に使用されていることを確認するようにしてください。大幅に変更されたデータ構造体とフィールドの一覧を下に示します。

buf 構造体の変更内容

下に一覧表示したフィールドは転送サイズに関係しています。転送サイズは 4G バイトを超えることができるようになりました。

size_t        b_bcount;          /* was type unsigned int */
size_t        b_resid;           /* was type unsigned int */
size_t        b_bufsize;         /* was type long */

ddi_dma_attr

ddi_dma_attr(9S) 構造体は、DMA エンジンとデバイスの属性を定義します。これらの属性はレジスタサイズを指定するため、基本型の代わりに固定幅データ型が使用されるようになりました。

ddi_dma_cookie 構造体の変更内容

uint32_t     dmac_address;    /* was type unsigned long */
size_t       dmac_size;       /* was type u_int */

ddi_dma_cookie(9S) 構造体には 32 ビットの DMA アドレスが含まれているため、アドレスの定義には固定幅データ型が使用されるようになりました。サイズは、size_t として定義し直されました。

csi_arq_status 構造体の変更内容

uint_t    sts_rqpkt_state;         /* was type u_long */
uint_t    sts_rqpkt_statistics;    /* was type u_long */

構造体内のこれらのフィールドは拡張する必要がなく、32 ビット量として定義し直されました。

scsi_pkt 構造体の変更内容

uint_t      pkt_flags;           /* was type u_long */
int     pkt_time;        /* was type long */
ssize_t     pkt_resid;           /* was type long */
uint_t      pkt_state;           /* was type u_long */
uint_t      pkt_statistics;      /* was type u_long */

scsi_pkt(9S) 構造体内の pkt_flags、pkt_state、および pkt_statistics フィールドは拡張する必要がないため、これらのフィールドは 32 ビット整数として定義し直されました。データ転送サイズの pkt_resid フィールドは拡張するため、ssize_t として定義し直されました。

DDI 関数の変更された引数を確認する

このセクションでは、変更された DDI 関数の引数のデータ型について説明します。

getrbuf() 引数の変更内容

struct buf *getrbuf(int sleepflag);

前のリリースでは、sleepflag は long 型として定義されました。

drv_getparm() 引数の変更内容

int drv_getparm(unsigned int parm, void *value_p);

前のリリースでは、value_p は unsigned long 型として定義されました。64 ビットカーネルでは、drv_getparm(9F) は 32 ビット量と 64 ビット量の両方を取得できます。インタフェースではこれらの量のデータ型は定義されないので、単純なプログラミングエラーが起こる可能性があります。

次の新しいルーチンのほうが安全です。

clock_t       ddi_get_lbolt(void);
time_t        ddi_get_time(void);
cred_t        *ddi_get_cred(void);
pid_t         ddi_get_pid(void);

ドライバの作成者は、できるかぎり drv_getparm(9F) の代わりにこれらのルーチンを使用してください。

delay() および timeout() 引数の変更内容

void delay(clock_t ticks);
timeout_id_t timeout(void (*func)(void *), void *arg, clock_t ticks);

delay(9F) および timeout(9F) ルーチンの ticks 引数は、long から clock_t に変更されました。

rmallocmap() および rmallocmap_wait() 引数の変更内容

struct map *rmallocmap(size_t mapsize);
struct map *rmallocmap_wait(size_t mapsize);

rmallocmap(9F) および rmallocmap_wait(9F) ルーチンの mapsize 引数は、ulong_t から size_t に変更されました。

scsi_alloc_consistent_buf() 引数の変更内容

struct buf *scsi_alloc_consistent_buf(struct scsi_address *ap,
    struct buf *bp, size_t datalen, uint_t bflags,
    int (*callback )(caddr_t), caddr_t arg);

前のリリースでは、datalen は int として定義され、bflags は ulong として定義されました。

uiomove() 引数の変更内容

int uiomove(caddr_t address, size_t nbytes,
    enum uio_rw rwflag, uio_t *uio_p);

nbytes 引数は long 型として定義されていましたが、nbytes はサイズをバイト単位で表すため、size_t のほうが適切です。

cv_timedwait() および cv_timedwait_sig() 引数の変更内容

int cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t timeout);
int cv_timedwait_sig(kcondvar_t *cvp, kmutex_t *mp,    clock_t timeout);

前のリリースでは、cv_timedwait(9F) および cv_timedwait_sig(9F) ルーチンの timeout 引数は、long 型として定義されました。これらのルーチンは時間を ticks で表すため、clock_t のほうが適切です。

ddi_device_copy() 引数の変更内容

int ddi_device_copy(ddi_acc_handle_t src_handle,
    caddr_t src_addr, ssize_t src_advcnt,
    ddi_acc_handle_t dest_handle, caddr_t dest_addr,
    ssize_t dest_advcnt, size_t bytecount, uint_t dev_datasz);

src_advcnt dest_advcntdev_datasz の各引数は、型が変更されました。これらの引数は、以前はそれぞれ long、long、および ulong_t として定義されました。

ddi_device_zero() 引数の変更内容

int ddi_device_zero(ddi_acc_handle_t handle,
    caddr_t dev_addr, size_t bytecount, ssize_t dev_advcnt,
    uint_t dev_datasz):

前のリリースでは、dev_advcnt は long 型として、dev_datasz は ulong_t 型として定義されていました。

ddi_dma_mem_alloc() 引数の変更内容

int ddi_dma_mem_alloc(ddi_dma_handle_t handle,
    size_t length, ddi_device_acc_attr_t *accattrp,
    uint_t flags, int (*waitfp)(caddr_t), caddr_t arg,
    caddr_t *kaddrp, size_t *real_length,
    ddi_acc_handle_t *handlep);

前のリリースでは、 lengthflags、および real_length は、uint_t、ulong_t、および uint_t * 型で定義されていました。

データ共有を処理するルーチンを変更する

デバイスドライバが long またはポインタを含むデータ構造体を、ioctl(9E)devmap(9E)、または mmap(9E) を使用する 32 ビットアプリケーションと共有し、そのドライバが 64 ビットカーネル用に再コンパイルされている場合、データ構造体のバイナリレイアウトは互換性がありません。フィールドが現在 long 型で定義され、64 ビットのデータ項目が使用されていない場合は、32 ビット量のままのデータ型を使用するようにデータ構造体を変更します (int および unsigned int)。それ以外の場合、ドライバは、ILP32 と LP64 の構造体の形状の違いを認識して、アプリケーションとカーネルの間でモデルの不一致が発生しているかどうかを判定する必要があります。

データモデルの潜在的な相違を処理するには、ユーザーアプリケーションと直接対話する ioctl()devmap()、および mmap() の各ドライバエントリポイントを記述して、引数がカーネルと同じデータモデルを使用するアプリケーションから来たかどうかを判定する必要があります。

ioctl() でのデータ共有

モデルの不一致がアプリケーションとドライバの間に存在するかどうかを判定するために、ドライバは FMODELS マスクを使用して、ioctl () mode 引数からモデル型を判定します。次の値で OR を使用してモードを有効にし、アプリケーションのデータモデルを特定します。

  • FLP64 – アプリケーションは LP64 データモデルを使用します

  • FILP32 – アプリケーションは ILP32 データモデルを使用します

64 ビットに対応したデバイスドライバに対する入出力制御のサポートのコード例は、ddi_model_convert_from(9F) を使用してこの状況を処理する方法を示しています。

devmap() でのデータ共有

64 ビットドライバと 32 ビットアプリケーションでメモリーを共有できるようにするには、64 ビットドライバで生成されるバイナリレイアウトが 32 ビットアプリケーションで消費されるレイアウトと同じである必要があります。アプリケーションにエクスポートされるマッピング済みのメモリーには、データモデル依存のデータ構造体を含める必要があることがあります。

カーネルのデータモデルが変わってもデバイスレジスタのサイズは変わらないため、この問題に直面するメモリーマッピングデバイスはほとんどありません。ただし、ユーザーアドレス空間にマッピングをエクスポートする一部の擬似デバイスでは、別のデータ構造体を ILP32 または LP64 アプリケーションにエクスポートできます。データモデルの不一致が発生しているかどうかを判定するために、devmap(9E)model パラメータを使用して、アプリケーションが予期するデータモデルを記述します。model パラメータは、次のいずれかの値に設定されます。

  • DDI_MODEL_ILP32 – アプリケーションは ILP32 データモデルを使用します

  • DDI_MODEL_LP64 – アプリケーションは LP64 データモデルを使用します

model パラメータは、変換せずに ddi_model_convert_from(9F) ルーチンまたは STRUCT_INIT() に渡すことができます。32 ビットと 64 ビットのデータ構造体マクロを参照してください。

mmap() でのデータ共有

mmap(9E) にはデータモデル情報を渡すために使用できるパラメータがないため、ドライバの mmap(9E) エントリポイントを記述して、新しい DDI 関数 ddi_model_convert_from(9F) を使用できます。この関数は、次のいずれかの値を返して、アプリケーションのデータ型モデルを示します。

  • DDI_MODEL_ILP32 – アプリケーションは ILP32 データモデルを予期します

  • DDI_MODEL_ILP64 – アプリケーションは LP64 データモデルを予期します

  • DDI_FAILURE – 関数は mmap(9E) から呼び出されませんでした

ioctl() および devmap() の場合と同様、モデルビットを ddi_model_convert_from(9F) に渡してデータ変換が必要かどうかを判定するか、モデルを STRUCT_INIT() に渡します。

または、devmap(9E) エントリポイントをサポートするようにデバイスドライバを移行します。

x86 ベースのプラットフォームで 64 ビット Long データ型の構造体を確認する

x86 プラットフォームでは、uint64_t などの 64 ビット long 型を使用する構造体を慎重に確認してください。配置やサイズが、32 ビットモードと 64 ビットモードのコンパイルで異なることがあります。次のような例を考えます。

#include &lt;studio>
#include &ltsys>

struct myTestStructure {
        uint32_t        my1stInteger;
        uint64_t        my2ndInteger;
};

main()
{
        struct myTestStructure a;

        printf("sizeof myTestStructure is: %d\n", sizeof(a));
        printf("offset to my2ndInteger is: %d\n", (uintptr_t)&a.bar - (uintptr_t)&a);
}

32 ビットシステムでは、この例の場合は次の結果が表示されます。

sizeof myTestStructure is: 12
offset to my2ndInteger is: 4

逆に、64 ビットシステムでは、この例の場合は次の結果が表示されます。

sizeof myTestStructure is: 16
offset to my2ndInteger is: 8

したがって、32 ビットアプリケーションと 64 ビットアプリケーションでは構造体が異なります。その結果、同じ構造体を 32 ビット環境と 64 ビット環境の両方で実行しようとすると、問題が発生することがあります。この状況は、特に構造体が ioctl() 呼び出しを介してカーネルとの間で受け渡しされる場合によく起こります。