윈도우 커널모드 드라이버 64비트 포팅

Download Report

Transcript 윈도우 커널모드 드라이버 64비트 포팅

윈도우 커널모드 드라이버
64비트 포팅
월간 마이크로소프트웨어 2005.9
김성현
2006.1.21
목차






64비트 세상
제품 포팅 계획
64비트 CPU
64비트 드라이버 빌드
DDK 예제 포팅
포팅 문제 해결
64비트 세상

INTEL P4 프레스캇 630 – 3GHz





System Bus : 800MHz
L1 Cache : 16KB
L2 Cache : 2MB
64Bit 지원( EM64T )
하이퍼스레딩 기술 지원
64비트 드라이버 포팅! Why?

64비트 윈도우 정책


WOW64




32비트 APP 지원, 32비트 Driver 지원안함
32비트 APP 에뮬레이션 레이어
32비트 APP 바이너리 그대로 수행
32비트 Driver 동작 못함
64비트 드라이버 바이너리 필요!!!

32비트, 64비트 One Source, Two Binary
NT Executive
Win32k.sys
Kernel Mode
User Mode
Reserved Address Space
0x00000000`7FFEFFFF or
0x00000000`FFFEFFFF
64-bit ntdll.dll
WoW64.dll
WoW64win.dll
WoW64cpu.dll
32-bit ntdll.dll
32-bit modules
제품 포팅 계획
32비트 APP
64비트 APP
유저모드
커널모드
64비트 Driver
방법1
(제약사항있음)
64비트 Driver
방법2
FileSystem Redirection


32비트 프로세스로 제품설치시 주의
32비트 APP 접근시 WOW64에서 처리



C:\Program Files => C:\Program
Files(x86)
C:\windows\system32 =>
C:\windows\syswow64
Wow64DisableWow64FsRedirection()
API 로 제어가능
Registry Redirection

32비트 APP 접근시 WOW64에서 처리



HKLM\Software =>
HKLM\Software\Wow6432Node
HKCR => HKCR\Wow6432Node
RegOpenKeyEx(), RegCreateKeyEx()


KEY_WOW64_64KEY: 64bit Registry 접근
KEY_WOW64_32KEY: 32bit Registry 접근
64비트 CPU 종류

AMD64 ( AMD )



IA64 ( INTEL )



진보적인 아키텍쳐(RISC 기반, 32비트 호환?)
Itanium, Itanium2
EM64T ( INTEL )



32비트 호환
Athlon64, Opteron
AMD64 Clone? 개발시 AMD64로 취급
ZEON EM64T, Pentium4 with EM64T
Q) Dual P4 Hiper Threading EM64T
CPU별 바이너리 종류
지원 대상 CPU
생성할 바이너리 종류
AMD Opteron
AMD64 바이너리
AMD Athlon64
AMD64 바이너리
Intel Xeon EM64T
AMD64 바이너리
Intel Pentium 4 with EM64T
AMD64 바이너리
Intel Itanium processors
(including the Itanium2)
IA64 바이너리
64비트 드라이버 빌드 DDK


64비트 빌드환경을 제공하는 DDK
Windows Server 2003 SP1 DDK



http://www.microsoft.com/whdc/devtool
s/ddk/default.mspx CD 신청
http://www.microsoft.com/whdc/driver/
wdf/KMDF_pkg.mspx Download 가능
Thanks OSR!!!
64비트 드라이버 빌드

32비트 드라이버


NT4 빌드? 2K 빌드? XP 빌드?
64비트 드라이버

2K3 빌드! ( XP x64 Edition 커널 )
64비트 드라이버 빌드
DDK 빌드 종류
용도
Windows Server 2003 Checked
IA-64 Bit Build Environment
Windows Server 2003 Free IA-64
Bit Build Environment
IA64용
디버그버전 빌드
IA64용
릴리즈버전 빌드
Windows Server 2003 Checked
x64 Build Environment
AMD64, EM64T
디버그버전 빌드
Windows Server 2003 Free x64
Build Environment
AMD64, EM64T
릴리즈버전 빌드
IA64 define : _M_IA64, __IA64__ ; x64 define : _M_AMD64, __AMD64__
64비트 Windows




Windows XP x64 Edition
Windows XP 64bit Edition
Windows Server 2003 x64 Edition
Windows Server 2003 for Itanium
based system
인라인 어셈블리






OH NO!!!
X86 instruction
AMD64 instruction
IA64 instruction
64비트 컴파일러는 인라인 어셈블리 미
지원
원칙 : 1 C-Source Code, 3 Binary
AMD64 Instruction









mov
[rbp-0x80],rcx
mov
rdi,[rdi+0x18]
movzx ecx,byte ptr [rax+rdi]
sub
rsp,rcx
and
rsp,0xfffffffffffffff0
mov
rdi,rsp
mov
rsi,[rbp+0x100]
add
rsi,0x28
test byte ptr [rbp+0xf0],0x1
IA64 Instruction









addl r2=ffffffff`ffe020b8, gp ;;
ld8 r2=[r2]
nop.i 0 ;;
ld8 r3=[r2], 8 ;;
ld8 gp=[r2]
mov b6=r3, +0
nop.m 0
nop.i 0
br.cond.sptk.few b6
64비트 포팅 가이드라인



_WIN64 define ( _WIN32 )
__AMD64__, __IA64__
포인터 사용 체크


포인터 연산 체크


sizeof(PULONG) != sizeof(ULONG)
32비트 + 32비트 = 64비트
0xFFFFFFFF != -1
64비트 포팅 가이드라인



~((UINT64)(PAGE_SIZE-1)) == (UINT64)~(PAGE_SIZE-1)
PAGE_SIZE = 0x1000UL // Unsigned Long - 32 bits
PAGE_SIZE - 1 = 0x00000fff
LHS expression:
// Unsigned expansion(UINT64)(PAGE_SIZE 1 ) = 0x0000000000000fff
~((UINT64)(PAGE_SIZE -1 )) = 0xfffffffffffff000

RHS expression:
~(PAGE_SIZE-1) = 0xfffff000
(UINT64)(~(PAGE_SIZE - 1)) = 0x00000000fffff000

~((UINT64)(PAGE_SIZE-1)) != (UINT64)(~(PAGE_SIZE-1))

드라이버 포팅 예제

NT4 DDK 샘플


64비트 빌드환경



DDK\src\general\portio
Windows Server 2003 Checked IA-64(또
는 x64) 실행
일반샘플처럼 빌드
생성된 Buildchk_wnet_IA64(또는
AMD64).err 확인
드라이버 포팅 예제
오류내용







1>sys\genport.c(223) : error C4312: 'type cast' :
conversion from 'ULONG' to 'PVOID' of greater size
1>sys\genport.c(513) : error C4311: 'type cast' : pointer
truncation from 'PVOID' to 'ULONG'
1>sys\genport.c(526) : error C4311: 'type cast' : pointer
truncation from 'PVOID' to 'ULONG'
1>sys\genport.c(526) : error C4312: 'type cast' :
conversion from 'unsigned long' to 'PUCHAR' of greater size
1>sys\genport.c(530) : error C4311: 'type cast' : pointer
truncation from 'PVOID' to 'ULONG'
1>sys\genport.c(530) : error C4312: 'type cast' :
conversion from 'unsigned long' to 'PUSHORT' of greater
size
1>sys\genport.c(534) : error C4311: 'type cast' : pointer
truncation from 'PVOID' to 'ULONG'
포인터의 타입캐스팅

데이터 타입의 크기에 대한 문제





32비트
32비트
64비트
64비트
환경
환경
환경
환경
포인터 크기: 32비트
ULONG 크기: 32비트
포인터 크기: 64비트
ULONG 크기: 32비트
포인터와 ULONG을 구분하지 않고 작성
된 코드에 대한 문제
포인터의 타입캐스팅







1>sys\genport.c(513) : error C4311: 'type cast' :
pointer truncation from 'PVOID' to 'ULONG‘
511 : if (nPort >= pLDI->PortCount ||
512 : (nPort + DataBufferSize) > pLDI->PortCount ||
513 : (((ULONG)pLDI->PortBase + nPort) &
(DataBufferSize - 1)) != 0)
pLDI->PortBase는 PVOID, nPort 는 ULONG
타입캐스팅이 에러?
포인터의 타입캐스팅

해결방법 1



513 : (((ULONG_PTR)pLDI->PortBase + nPort) &
(DataBufferSize - 1)) != 0)
ULONG_PTR



ULONG 형 포인터? X
32비트 빌드시: 32비트 크기의 정수형
64비트 빌드시: 64비트 크기의 정수형
포인터의 타입캐스팅

해결방법 2



PtrToUlong() 매크로


513 : ((PtrToUlong(pLDI->PortBase) + nPort) &
(DataBufferSize - 1)) != 0)
#define PtrToUlong( p )
((ULONG)(ULONG_PTR) (p) )
주의) pLDI->PortBase가 32비트로 잘림
포인터의 타입캐스팅


1>sys\genport.c(526) : error C4311: 'type cast' : pointer
truncation from 'PVOID' to 'ULONG'
1>sys\genport.c(526) : error C4312: 'type cast' : conversion from
'unsigned long' to 'PUCHAR' of greater size

524 : case IOCTL_GPD_READ_PORT_UCHAR:
525:
*(PUCHAR)pIOBuffer = READ_PORT_UCHAR(
526:
(PUCHAR)((ULONG)pLDI->PortBase + nPort) );
527:
break;

한줄에 2가지 오류



포인터의 타입캐스팅

방법 1


방법 2



(PUCHAR)PtrToUlong(pLDI->PortBase) + nPort);
방법 2+


(PUCHAR)((ULONG_PTR)pLDI->PortBase + nPort));
(PUCHAR)UlongToPtr(PtrToUlong(pLDI->PortBase) +
nPort));
신중히 결정할 것
주의) 반복되는 오류처리
타입캐스팅을 넘어서





1>sys\genport.c(223) : error C4312: 'type cast' :
conversion from 'ULONG' to 'PVOID' of greater size
223 : pLocalInfo->PortBase =
(PVOID)MappedAddress.LowPart;
MappedAddress.LowPart 는 ULONG
타입캐스팅으로 해결? 아니면?
타입캐스팅을 넘어서

해결방법 1


223 : pLocalInfo->PortBase =
UlongToPtr( MappedAddress.LowPart );
해결방법 2

223 : pLocalInfo->PortBase =
(PVOID)(ULONG_PTR)MappedAddress.LowPart;
타입캐스팅을 넘어서

해결방법 3



데이터구조를 생각하자
LowPart? HighPart?
MappedAddress의 타입


PHYSICAL_ADDRESS MappedAddress;
typedef LARGE_INTEGER
PHYSICAL_ADDRESS,
*PPHYSICAL_ADDRESS;
타입캐스팅을 넘어서











typedef union _LARGE_INTEGER {
struct {
ULONG LowPart;
LONG HighPart;
};
struct {
ULONG LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
// 64비트!!!
} LARGE_INTEGER;
타입캐스팅을 넘어서

해결방법 3






223 : pLocalInfo->PortBase =
(PVOID)MappedAddress.QuadPart;
198: HalTranslateBusAddress( Isa,
199:
0,
200:
PortAddress,
201:
&MemType,
202:
&MappedAddress );
응용 프로그램과 드라이버가
공유하는 구조체


기존 32비트 응용프로그램이 사용하던
구조체를 새로 작성되는 64비트 드라이
버에 전달해야 할 때
32비트 환경과 64비트 환경에서 데이터
크기가 달라지는 타입에 대한 문제
응용 프로그램과 드라이버가
공유하는 구조체

응용 프로그램 코드 예제




GENPORT_WRITE_INPUT InputBuffer
sscanf(argv[2],"%x",&InputBuffer.PortNumber);
InputBuffer.LongData = (ULONG)DataValue;
드라이버 코드 예제



GENPORT_WRITE_INPUT *pInputBuffer
nPortNum = pInputBuffer->PortNumber;
nLongData = pInputBuffer->LongData;
응용 프로그램과 드라이버가
공유하는 구조체









Gpioctl.h
typedef struct _GENPORT_WRITE_INPUT {
ULONG PortNumber;
// Port # to write to
union {
// Data to be output to port
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
} GENPORT_WRITE_INPUT;
응용 프로그램과 드라이버가
공유하는 구조체

32비트 APP + 64비트 DRV
32 비트 gpdwrite.exe
GENPORT_WRITE
_INPUT
(ULONG 4 바이트,
union 4 바이트)
64 비트 genport.sys
GENPORT_WRITE
_INPUT
(ULONG 4 바이트,
union 4 바이트)
공유 구조체 문제









32비트, 64비트에서 크기가 다른 데이터가 존
재하는 경우
typedef struct _GENPORT_WRITE_INPUT {
PVOID PortNumber;
union {
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
} GENPORT_WRITE_INPUT;
공유 구조체 문제

32비트 APP + 64비트 DRV
32 비트 gpdwrite.exe
GENPORT_WRITE
_INPUT
(PVOID 4 바이트,
union 4 바이트)
64 비트 genport.sys
GENPORT_WRITE
_INPUT
(PVOID 8 바이트,
union 4 바이트)
공유 구조체 문제 해결










64비트 드라이버가 사용하는 32비트용 구조
체 정의
typedef struct _GENPORT_WRITE_INPUT_32
{
ULONG PortNumber;
union {
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
} GENPORT_WRITE_INPUT;
공유 구조체 문제 해결

32비트 APP + 64비트 DRV
32 비트 gpdwrite.exe
GENPORT_WRITE
_INPUT
(PVOID 4 바이트,
union 4 바이트)
64 비트 genport.sys
GENPORT_WRITE
_INPUT_32
(ULONG 4 바이트,
union 4 바이트)
IoIs32bitProcess()

시나리오




32비트 기존 응용프로그램 사용
64비트 드라이버 출시 후…
64비트 응용프로그램 포팅 완료
64비트 드라이버의 운명은?


32 APP, 64 APP 모두 지원
32 APP, 64 APP 구분 필요
IoIs32bitProcess()

64비트 APP + 64비트 DRV
64 비트 gpdwrite.exe
GENPORT_WRITE
_INPUT
(PVOID 8 바이트,
union 4 바이트)
64 비트 genport.sys
GENPORT_WRITE
_INPUT
(PVOID 8 바이트,
union 4 바이트)
IoIs32bitProcess()



















GENPORT_WRITE_INPUT *pInputBuffer
GENPORT_WRITE_INPUT_32 *pInputBuffer32
If (IoIs32bitProcess( Irp ) == TRUE)
pInputBuffer32 = (GENPORT_WRITE_INPUT_32*) pIrp->AssociatedIrp.SystemBuffer;
else
pInputBuffer = (GENPORT_WRITE_INPUT*) pIrp->AssociatedIrp.SystemBuffer;
switch (IoctlCode)
{
case IOCTL_GPD_WRITE_PORT_ULONG:
If (IoIs32bitProcess( Irp ) == TRUE)
{
nPortNum = pInputBuffer32->PortNumber;
nLongData = pInputBuffer32->LongData;
}
Else // 64 bit
{
nPortNum = pInputBuffer->PortNumber;
nLongData = pInputBuffer->LongData;
}
IoIs32bitProcess()

IOCTL Code 사용하는 방법

현재
Device Type Access
(16)
(2)

Custom Function Method
(1)
(11)
(2)
64비트
Device
Access Custom 64-Bit
Type (16) (2)
(1)
(1)
Function Method
(10)
(2)
Quiz















#pragma pack(1)
typedef struct _PACK1_DATA
{
ULONG ul1;
UCHAR uc1;
} PACK1_DATA;
#pragma pack()
#pragma pack(8)
typedef struct _PACK8_DATA
{
UCHAR
uc8;
PACK1_DATA p1d;
ULONG
ul8;
} PACK8_DATA;
#pragma pack()
Quiz


32비트 환경에서 메모리 구성은?
1
uc8 padding

uc1 padding
2
uc8

ul1
ul1
uc1padding ul8
3
uc8 padding
ul1
uc1
ul8
ul8
Data Misalignment 문제




IA64에서 발생하는 문제
메모리 주소 경계에 맞지 않는 주소를 액세스
할 경우
APP => 비정상종료
DRV => 블루스크린


Bug Check 0x1E:
KMODE_EXCEPTION_NOT_HANDLED
Parameter 1: 0x80000002
STATUS_DATATYPE_MISALIGNMENT
Data Misalignment 문제











#pragma pack(1)
typedef struct _GENPORT_WRITE_INPUT {
BOOLEAN bTest;
PVOID PortNumber;
// Port # to write to
union {
// Data to be output to port
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
} GENPORT_WRITE_INPUT;
#pragma pack()
Data Misalignment 문제

1바이트 정렬된 구조체의 메모리구조
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12
bTest
PortNumber
LongData
Data Misalignment 문제 해결



1. 예외 핸들링
2. 구조체를 정렬된 형태로 수정
3. 컴파일러에게 도움 요청
Data Misalignment 문제 해결


구조체 선언시 #pragma pack(1) 제거
8바이트 정렬된 구조체의 메모리구조
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
bTest
Padding
PortNumber
LongData
Data Misalignment 문제 해결










메모리 공간 절약 배치
typedef struct _GENPORT_WRITE_INPUT {
PVOID PortNumber;
// Port # to write to
union {
// Data to be output to port
ULONG LongData;
USHORT ShortData;
UCHAR CharData;
};
BOOLEAN bTest;
} GENPORT_WRITE_INPUT;
Data Misalignment 문제 해결

8바이트 정렬하고 bTest를 맨뒤로…
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12
PortNumber
LongData bTest
Data Misalignment 문제 해결

컴파일러에게 도움 요청



죽어도 #pragma pack(1) 을 써야할 경우
32비트 응용 프로그램의 구조체를 수정하
지 못할 경우
UNALIGNED 매크로



문제가 있는 코드임을 컴파일러에게 알림
1바이트씩 읽어서 조합해 주는 코드 생성
주의) 성능 저하의 우려가 있음
Data Misalignment 문제 해결

데이터 미정렬 예외(data misalignment
exception)가 발생하는 코드


pInputBuffer->PortNumber = p;
데이터 미정렬 예외(data misalignment
exception)가 발생하지 않는 코드


*(UNALIGNED PVOID)
&pInputBuffer->PortNumber = p;
참고자료

DDK Help: 64-Bit Issues


드라이버 포팅 기본 가이드
MS 사이트 64-bit Platform


http://www.microsoft.com/whdc/syste
m/platform/64bit/default.mspx
64비트 개발관련 수많은 문서
Q&A