2016-09-12 32 views
-1

Cでメモリを読み書きする方法はすべてわかっています。ポインタを持っており、メモリへの読み書きをしたいときに逆参照します。下位レベルでは、コントロールバスは、コントロールバス上のIO/PORTラインを設定しないことによってメモリからの読み出し要求を処理し、メモリコントローラがメモリを読み書きできるようにします。 しかし、ポート・アドレスではポート・アドレスが使用され、それらに対する読取り/書込みは制御バスがIO/PORTラインを設定することを除いてメモリの読取り/書込みと同じプロセスであるため、メモリ・コントローラは現在のバスを無視しIOコントローラは注意を払う。 x86 ASMでは、INとOUTの命令があります。しかし、Cでこれを行う方法はありますか?私はWindows 7/10(win32)環境にいます。特定のヘッダー(ウィンドウの検索に問題があります)では、_inpやinpなどの関数が存在する可能性がありますが、asmのIN命令とOUT命令と同じことを行う方法はありますか? (EDIT:INとOUTはリング0命令なので、インラインアセンブリーは使用できません)Cで制御バス上にIO/PORTラインを設定する方法

+0

Uhhh ...インラインasm? – stackptr

+0

@stackptr INとOUTはリング0の優先順位付き命令です。それらを使用できる唯一の場所はWindowsカーネルです。典型的なWindowsアプリケーションは、リング3と4で動作します。 –

+0

私は、現代のOSでは、これらのトラフ自身のデバイスドライバにアクセスできます(通常の想定方法で、あなたのコンテキストを利用したバグを数えずに)。 (私はOSがどのようにミックスのようなものがこれを解決しているのかわからない。ドライバはリング0でもないが、Linux OSやMSウィンドウのものにとどまっている限りドライバはほとんどが0のIIRCである) – Ped7g

答えて

3

Cは、どのようにIOを実行するかを含め、どのプラットフォームからも完全に抽象です。

WindowsにはREAD_PORT_XXXがあります。

あなたのいずれかが必要これらの機能を使用することができるようにするために:

  1. CPL(現行特権レベルが)これはまたリング0として知られて0に等しいです。値3
  2. IOPL(IO特権レベル)IOマップに設定された適切なビット。

最後の2つは、私が覚えている限り、Windowsではサポートされていません。
最初のものはデバイスドライバでしか実現できません。現代のOSはユーザーモードプログラムに(IOアクセスを含む)カーネル特権を与えません。


あなたは、読み取り/書き込みがIOのオフセット書き込み/読み出しに対応するポートへのアクセスを読み書きするための64KiB Control Device Objectへのアクセスを変換する非常にシンプルなドライバを書くことができます。

唯一必要なものはWDK/DDKです。

幸運にも、私はそのようなドライバソースを手にしています。
私はこれを書いたときに覚えていないので、塩の粒でそれを取る。

#include <ntddk.h> 
#include <wdf.h> 
#include <Wdmsec.h> 


VOID IomemIoEvtIoRead (WDFQUEUE Queue, WDFREQUEST Request, size_t Length); 
VOID IomemIoEvtIoWrite (WDFQUEUE Queue, WDFREQUEST Request, size_t Length); 
NTSTATUS IoMemIoReadWrite(WDFREQUEST Request, size_t Length, ULONG_PTR* copied);   


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) 
{ 
    WDF_DRIVER_CONFIG config; 
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    WDFDRIVER driver = NULL; 

    PWDFDEVICE_INIT deviceInit = NULL; 
    WDFDEVICE device = NULL; 

    WDF_IO_QUEUE_CONFIG queueConfig; 
    WDFQUEUE queue = NULL; 

    UNICODE_STRING acl, deviceName; 

    WDF_DRIVER_CONFIG_INIT(&config, NULL); 
    status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config,&driver); 
    if (!NT_SUCCESS(status)) 
     return STATUS_UNSUCCESSFUL; 

    /////////////////////////////////////// 


    RtlInitUnicodeString(&acl, L"D:P(A;;GA;;;WD)"); 
    RtlInitUnicodeString(&deviceName, L"\\Device\\iomem_io"); 

    status = STATUS_UNSUCCESSFUL; 
    deviceInit = WdfControlDeviceInitAllocate(driver, (PCUNICODE_STRING)&acl); 

    if (deviceInit) 
     status = WdfDeviceInitAssignName(deviceInit, (PCUNICODE_STRING)&deviceName); 

    if (NT_SUCCESS(status)) 
     status = WdfDeviceCreate(&deviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); 

    if (!NT_SUCCESS(status)) 
    { 
     WdfDeviceInitFree(deviceInit); 
     return status; 
    } 

    WdfControlFinishInitializing(device); 

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential); 

    queueConfig.EvtIoRead = IomemIoEvtIoRead; 
    queueConfig.EvtIoWrite = IomemIoEvtIoWrite; 



    return WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue); 
} 




VOID IomemIoEvtIoRead (WDFQUEUE Queue, WDFREQUEST Request, size_t Length) 
{ 
    ULONG_PTR bytesCopied = 0; 
    NTSTATUS status = IoMemIoReadWrite(Request, Length, &bytesCopied); 

    WdfRequestCompleteWithInformation(Request, status, bytesCopied); 
} 


VOID IomemIoEvtIoWrite (WDFQUEUE Queue, WDFREQUEST Request, size_t Length) 
{ 
    ULONG_PTR bytesCopied = 0; 
    NTSTATUS status = IoMemIoReadWrite(Request, Length, &bytesCopied); 

    WdfRequestCompleteWithInformation(Request, status, bytesCopied);  
} 


NTSTATUS IoMemIoReadWrite(WDFREQUEST Request, size_t Length, ULONG_PTR* copied) 
{ 
    NTSTATUS    status = STATUS_UNSUCCESSFUL; 
    WDF_REQUEST_PARAMETERS params; 
    size_t     io_length; 
    LONGLONG    port; 
    LONG     data; 
    PVOID     buffer; 
    WDFMEMORY    memory; 

    WDF_REQUEST_PARAMETERS_INIT(&params); 
    WdfRequestGetParameters(Request, &params); 
    if (params.Type == WdfRequestTypeRead) 
    { 
     io_length = params.Parameters.Read.Length; 
     port = (LONGLONG)params.Parameters.Read.DeviceOffset; 
    } 
    else if (params.Type == WdfRequestTypeWrite) 
    { 
     io_length = params.Parameters.Write.Length; 
     port = (LONGLONG)params.Parameters.Write.DeviceOffset; 
    } 
    else 
     return status; 

    if (io_length > 4 || io_length == 3 || io_length > Length) 
     return status; 

    status = WdfRequestRetrieveOutputMemory(Request, &memory); 
    if (!NT_SUCCESS(status)) 
     return status; 

    buffer = WdfMemoryGetBuffer(memory, NULL); 

    if (params.Type == WdfRequestTypeRead) 
    { 
     switch (io_length) 
     { 
      case 1: data = READ_PORT_UCHAR((PUCHAR)port); break; 
      case 2: data = READ_PORT_USHORT((PUSHORT)port); break; 
      case 4: data = READ_PORT_ULONG((PULONG)port); break;  
      default: return STATUS_UNSUCCESSFUL; 
     } 

     RtlCopyMemory(&data, buffer, io_length); 
    } 
    else 
    { 
     RtlCopyMemory(buffer, &data, io_length); 

     switch (io_length) 
     { 
      case 1: WRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)data); break; 
      case 2: WRITE_PORT_USHORT((PUSHORT)port, (USHORT)data); break; 
      case 4: WRITE_PORT_ULONG((PULONG)port, data); break;  
      default: return STATUS_UNSUCCESSFUL; 
     }  

    } 

    if (copied) 
     *copied = io_length; 

    return STATUS_SUCCESS; 
} 

あなたは(私はあなたに残し操作)あなたは、ファイル\Device\iomem_ioを開くXオフセットに追求し、ポートからの読み取り/書き込みにそれに読み取り/書き込みすることができますをコンパイルし、このドライバをロードした後X


あなたが回答に与えたバストポロジの説明は、最善のものと古いものです。
最新のバストポロジはより複雑で、特にPCIe以来、レガシーまたはプラットフォーム固有の機能(電源管理など)のみがIOアドレス空間を介してアクセスされます。
他のすべてはメモリマップされたIOです。

3

最小のカーネルドライバーがない場合:いいえ、できません。

Cには、INおよびOUTアセンブラコマンドに近いメカニズムはありません。以前はメモリマップされたI/Oをサポートしていないマシンでは初期段階では迷惑でしたが、かなり先読みで除外されていることが判明しました。今日のオペレーティングシステムのほとんどは、特権を持っているためオペレーション。

間接的にI/Oポートにアクセスするには、INまたはOUT要求を呼び出して0にするカーネルドライバが必要です。いくつかの例は、「直接I/O」を検索することによって見つけることができます。特定のタイミング要件やセキュリティをサポートする方法がないなど、かなりの数のユーザーにとって、ユーザー空間からのものを使用することは悪い習慣とみなされます。

Windowsコンパイラのいくつかのバージョンでは、直接ポートアクセスをサポートしているWindowsバージョンをターゲットにしていました。_inp()および_outp()ユーザプログラムから直接ポートアドレスを許可するライブラリ関数をサポートしていました。カーネルやデバイスドライバのコードではまだまだあります。

+0

まあ、その "nuissance"はいくつかの深刻な背景を持っていました。周辺アドレス用に貴重な64Kバイトのアドレス空間を無駄にすることはありませんでした。また、アドレス範囲をデコードするために外部ロジック(AND/NAND /などのような4つの単純なゲートを持つICを使用することが多い)を単純化しました。 – Olaf

+0

@Olaf私は、別々のI/Oとメモリアドレススペースが迷惑であるとは言いませんでした(あなたが言うように、まれなアドレススペースをI/O用に複製するのがよい理由がありました)。 ) – tofro

+0

まあ、すべてのアーキテクチャがそれらを持っていたわけではない(そして、今日ではそれはx86上の伝統であり、埋め込まれているものでさえも)、それは問題になるだろう標準でそれらをサポートする。同様に、デフォルトで低レベルの機能を提供するようにCに指示します。周辺レジスタにアクセスするには、あまりにも多くの異なるメカニズムがあります(I/Oスペース対メモリマップだけでなく、後者は追加の手段が必要かもしれません)。このような低レベルのアクセスを忘れることはありません**常にアーキテクチャの独立した少数のドライバモジュールでカバーされるべきです。 – Olaf

関連する問題