윈도우 커널모드 드라이버 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 비트 윈도우 정책
32 비트 APP 지원 , 32 비트 Driver 지원안함 WOW64
32 비트 APP 에뮬레이션 레이어 32 비트 APP 바이너리 그대로 수행 32 비트 Driver 동작 못함
64 비트 드라이버 바이너리 필요 !!! 32 비트 , 64 비트 One Source, Two Binary
64-bit ntdll.dll64-bit ntdll.dll
WoW64.dllWoW64.dll WoW64win.dllWoW64win.dll
WoW64cpu.dllWoW64cpu.dll
Win32k.sysWin32k.sys
NT ExecutiveNT Executive
Kernel ModeKernel Mode
User ModeUser Mode
32-bit ntdll.dll32-bit ntdll.dll
32-bit modules32-bit modules
Reserved Address SpaceReserved Address Space0x00000000`7FFEFFFF or 0x00000000`7FFEFFFF or 0x00000000`FFFEFFFF0x00000000`FFFEFFFF
제품 포팅 계획
32 비트 APP
64 비트 Driver
64 비트 APP
64 비트 Driver
방법 1( 제약사항있
음 )
방법 2
유저모드커널모드
FileSystem Redirection 32 비트 프로세스로 제품설치시 주의 32 비트 APP 접근시 WOW64 에서 처리
C:\Program Files => C:\Program Files(x86) C:\windows\system32 => C:\windows\sys
wow64 Wow64DisableWow64FsRedirection() AP
I 로 제어가능
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 )
32 비트 호환 Athlon64, Opteron
IA64 ( INTEL ) 진보적인 아키텍쳐 (RISC 기반 , 32 비트 호환 ?) Itanium, Itanium2
EM64T ( INTEL ) 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/devtools/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 EnvironmentIA64 용
디버그버전 빌드Windows Server 2003 Free IA-
64 Bit Build EnvironmentIA64 용
릴리즈버전 빌드Windows Server 2003 Checked
x64 Build EnvironmentAMD64, EM64T
디버그버전 빌드Windows Server 2003 Free x64
Build EnvironmentAMD64, 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 bitsPAGE_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 샘플
DDK\src\general\portio 64 비트 빌드환경
Windows Server 2003 Checked IA-64( 또는 x64) 실행
일반샘플처럼 빌드 생성된 Buildchk_wnet_IA64( 또는 AMD
64).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 비트 32 비트 환경 ULONG 크기 : 32 비트 64 비트 환경 포인터 크기 : 64 비트 64 비트 환경 ULONG 크기 : 32 비트
포인터와 ULONG 을 구분하지 않고 작성된 코드에 대한 문제
포인터의 타입캐스팅 1>sys\genport.c(513) : error C4311: 'type cast' : pointer t
runcation 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
513 : ((PtrToUlong(pLDI->PortBase) + nPort) & (DataBufferSize - 1)) != 0)
PtrToUlong() 매크로 #define PtrToUlong( p ) ((ULONG)
(ULONG_PTR) (p) )
주의 ) pLDI->PortBase 가 32 비트로 잘림
포인터의 타입캐스팅 1>sys\genport.c(526) : error C4311: 'type cast' : pointer truncation fro
m 'PVOID' to 'ULONG' 1>sys\genport.c(526) : error C4312: 'type cast' : conversion from 'unsi
gned 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
(PUCHAR)((ULONG_PTR)pLDI->PortBase + nPort)); 방법 2
(PUCHAR)PtrToUlong(pLDI->PortBase) + nPort); 방법 2+
(PUCHAR)UlongToPtr(PtrToUlong(pLDI->PortBase) + nPort));
신중히 결정할 것 주의 ) 반복되는 오류처리
타입캐스팅을 넘어서 1>sys\genport.c(223) : error C4312: 'type cast' : conve
rsion 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_PT
R)MappedAddress.LowPart;
타입캐스팅을 넘어서 해결방법 3
데이터구조를 생각하자 LowPart? HighPart?
MappedAddress 의 타입 PHYSICAL_ADDRESS MappedAddress; typedef LARGE_INTEGER PHYSICAL_ADDRE
SS, *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
64 비트 genport.sys
32 비트 gpdwrite.exe
GENPORT_WRITE_INPUT
(ULONG 4 바이트 , union 4 바이트 )
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
64 비트 genport.sys
32 비트 gpdwrite.exe
GENPORT_WRITE_INPUT
(PVOID 4 바이트 , union 4 바이트 )
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
64 비트 genport.sys
32 비트 gpdwrite.exe
GENPORT_WRITE_INPUT
(PVOID 4 바이트 , union 4 바이트 )
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 비트 genport.sys
64 비트 gpdwrite.exe
GENPORT_WRITE_INPUT
(PVOID 8 바이트 , union 4 바이트 )
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 사용하는 방법
현재
64 비트Device Type (16)
Access (2)
Custom(1)
Function (11)
Method (2)
Device Type (16)
Access (2)
Custom(1)
64-Bit (1)
Function (10)
Method (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
2
3
uc8 padding ul1
uc1 ul8
uc8 padding ul8
padding
ul1
uc1
uc8 padding ul1
uc1 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 바이트 정렬된 구조체의 메모리구조
bTest
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12
PortNumber LongData
Data Misalignment 문제 해결 1. 예외 핸들링 2. 구조체를 정렬된 형태로 수정 3. 컴파일러에게 도움 요청
Data Misalignment 문제 해결 구조체 선언시 #pragma pack(1) 제거 8 바이트 정렬된 구조체의 메모리구조
bTest
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
PortNumber LongDataPadding
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 를 맨뒤로…
bTest
GENPORT_WRITE_INPUT
0 1 2 3 4 5 6 7 8 9 10 11 12
PortNumber LongData
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/system/platform/64bit/default.mspx
64 비트 개발관련 수많은 문서
Q & A
Top Related