понедельник, 31 января 2011 г.

Почему в Windows не может быть двух карт ввода-вывода одновременно

Карта ввода-вывода находится в TSS и описывает всё адресное пространство портов ввода/вывода, которое составляет 65536 портов, т.е. для описания всего адресного пространства понадобится 8Кб. 

TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта)

=============================================
Sel.    Base            Limit     DPL  P   G    Description
=============================================
0028  F7717D70  000020AB   0    P   1b   32-Bit TSS (Busy)
=============================================

#define INT_DIRECTION_MAP_SIZE   32
typedef UCHAR KINT_DIRECTION_MAP[INT_DIRECTION_MAP_SIZE];

#define IOPM_COUNT        1         // Number of i/o access maps that exist
#define IOPM_SIZE            8192    // Size of map callers can set.
#define PIOPM_SIZE          8196    // Size of structure we must allocate to hold it.

typedef struct _KiIoAccessMap
{
    KINT_DIRECTION_MAP DirectionMap;
    UCHAR IoMap[PIOPM_SIZE];
} KIIO_ACCESS_MAP;

В TSS лежит сама карта и смещение:

typedef struct _KTSS
{
...
   USHORT IoMapBase; // Смещение карты ввода/вывода от начала TSS.
   KIIO_ACCESS_MAP IoMaps[IOPM_COUNT];
...

Установить карту ввода-вывода для процесса можно с помощью ф-ции Ke386SetIoAccessMap, которая скопирует в TSS->IoMaps.IoMap пользовательскую карту и установит IoMapBase для процесса на нее.

Связь процесса с картой ввода-вывода:

typedef struct _KPROCESS
{
...
    USHORT IopmOffset; <== определено смещение на нее
    UCHAR Iopl;
...
} KPROCESS, *PKPROCESS, *PRKPROCESS;

При создании процесса вызывается ф-ция KeInitializeProcess:

VOID KeInitializeProcess( __out PRKPROCESS Process, __in KPRIORITY BasePriority, __in KAFFINITY Affinity, __in ULONG_PTR DirectoryTableBase[2], __in BOOLEAN Enable )
{
...
    //
    // Initialize IopmBase and Iopl flag for this process (i386 only)
    //

    Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE); <== IoMapBase будет указывать за пределы сегмента TSS
...
}

Для всех процессов(если для них не установлена карта ввода-вывода):

+0x000 Pcb             : _KPROCESS
...
+0x030 IopmOffset  : 0x20ac <== смещение за пределы сегмента
+0x032 Iopl             : 0 ''

А если учесть, что TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта), то получается, что любое обращение процесса к TSS будет генерить #GF, если не установлено 2 бита в eflags.

Итог:

IOPM для всех процессов может быть только один, причем по умолчанию смещение на карту находится на пределами сегмента TSS, однако ф-ция Ke386IoSetAccessProcess к примеру для конкретного процесса и для общей TSS устанавливает IopmOffset на общую карту доступа портов ввода-вывода.

А само смещение обновляется при переключении процессов(аттач/детач):

cPublicProc _KiSwapProcess  ,2
...
;
;   Change IOPM
;
    mov     ax,[edx]+PrIopmOffset
    mov     [ecx]+TssIoMapBase,ax
...

То есть в Windows просто не возможно иметь две и более разных карт ввода-вывода в TSS, места там хватает только для одной( limit == 20ABh ).

Комментариев нет:

Отправить комментарий