CPUID
CPUID opcode는 x86 아키텍처를 위한 프로세서 기계 명령어이다. (CPUID는 CPU IDentification에서 비롯한다.) 인텔이 펜티엄과 SL 강화 486 프로세서를 내세운 1993년에 도입하였다.[1]
CPUID opcode를 사용하여 소프트웨어는 프로세서 종류와 MMX/SSE와 같은 기능들을 결정할 수 있다. CPUID opcode는 0FA2h이며 EAX 레지스터 값은 어떠한 정보를 반환할지를 결정한다.
CPUID 명령을 일반적으로 사용하기까지 프로그래머들은 프로세서 메이커와 모델을 결정하기 위하여 CPU 동작에 사소한 차이를 불러일으킬 수 있는 비밀스러운 기계어를 기록하여야 했다.[2][3]
CPUID 호출
편집어셈블리어에서 CPUID 명령은 CPUID가 EAX 레지스터를 그대로 사용하므로 매개변수를 이용하지 않는다. EAX 레지스터는 어떠한 정보를 반환할지를 지정하는 값과 함께 로드되어야 한다. CPUID는 EAX = 0으로 먼저 호출되어야 하는데, 이는 CPU가 지원하는 가장 높은 호출 변수를 반환할 것이기에 그러한 것이다. 확장 정보를 가져오려면 CPUID는 비트 31의 EAX 집합을 사용하여 호출되어야 한다. 가장 높은 확장 명령 호출 변수를 결정하려면 EAX = 80000000h를 사용하여 CPUID를 호출하면 된다.
EAX=0: 제조업체 ID 가져오기
편집이것은 CPU의 제조업체 ID 문자열을 반환한다. 이 문자열은 EBX, EDX, ECX 순으로 저장된 12자리 아스키 문자열이다. 가장 높은 기본 호출 변수는 EAX로 반환된다.
다음은 잘 알려진 제조업체 ID 문자열이다.
"AMDisbetter!"
- AMD K5 프로세서 초기 샘플"AuthenticAMD"
- AMD"CentaurHauls"
- Centaur"CyrixInstead"
- 사이릭스"GenuineIntel"
- 인텔"GenuineTMx86"
,"TransmetaCPU"
- Transmeta"Geode by NSC"
- 내셔널 세미컨덕터"NexGenDriven"
- NexGen"RiseRiseRise"
- Rise"SiS SiS SiS "
- SiS"UMC UMC UMC "
- UMC"VIA VIA VIA "
- VIA
이를테면 GenuineIntel 프로세서 값은 EBX로 0x756e6547를, EDX로 0x49656e69를 ECX로 0x6c65746e로 반환된다.
.section .data
s0: .string "최대로 지원하는 표준 명령어 수: %i\n"
s1: .string "업체 ID: %s\n"
.text
.global main
.type main, @function
main:
push rbp
mov rbp, rsp
sub rsp, 16
xor eax, eax
mov [rsp + 12], eax
cpuid
mov [rsp], ebx
mov [rsp + 4], edx
mov [rsp + 8], ecx
mov edi, $s0
mov esi, eax
xor eax, eax
call printf
mov rdi, $s1
mov rsi, rsp
xor eax, eax
call printf
xor eax, eax
mov rsp, rbp
pop rbp
ret
C언어
#include <stdio.h>
#include <Windows.h>
int main (int argc, char** argv)
{
ULONG EAX_REG;
ULONG EBX_REG;
ULONG ECX_REG;
ULONG EDX_REG;
ULONG temp;
__asm{
pushad
mov eax,0
cpuid
mov EAX_REG, eax
mov EBX_REG, ebx
mov EDX_REG, edx
mov ECX_REG, ecx
popad
}
printf("0x%08x 0x%08x 0x%08x 0x%08x \n",EAX_REG,EBX_REG,EDX_REG,ECX_REG);
for(int i = 0; i < 4; i++)
{
temp = (EBX_REG & (0xFF << (8 * i)));
temp >>= (8 * i);
printf("%c",(char)temp);
}
for(int i = 0; i < 4; i++)
{
temp = (EDX_REG & (0xFF << (8 * i)));
temp >>= (8 * i);
printf("%c",(char)temp);
}
for(int i = 0; i < 4; i++)
{
temp = (ECX_REG & (0xFF << (8 * i)));
temp >>= (8 * i);
printf("%c",(char)temp);
}
printf("\n");
return 0;
}
EAX=1: 프로세서 정보 및 기능 비트
편집CPU의 스테핑, 모델, 계열 정보를 EAX(CPU 서명이라고도 불림)로, 기능 플래그를 EDX와 ECX로, 부가 기능 정보를 EBX로 반환한다.
EAX로 반환되는 정보 형태는 다음과 같다:
- 3:0 - 스테핑
- 7:4 - 모델
- 11:8 - 계열
- 13:12 - 프로세서 유형
- 19:16 - 확장 모델
- 27:20 - 확장 계열
EAX=2: 캐시 및 TLB 서술자 정보
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=3: 프로세서 일련 번호
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=80000000h: 가장 높은 확장 함수 가져오기
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=80000001h: 확장 프로세서 정보 및 기능 비트
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=80000002h,80000003h,80000004h: 프로세서 브랜드 문자열
편집프로세서 브랜드 문자열은 EAX, EBX, ECX, EDX로 반환된다.
.section .data
s0: .string "프로세서 브랜드 문자열: %s\n"
.text
.global main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
subq $48, %rsp
movl $0x80000002, %eax
cpuid
movl %eax, (%rsp)
movl %ebx, 4(%rsp)
movl %ecx, 8(%rsp)
movl %edx, 12(%rsp)
movl $0x80000003, %eax
cpuid
movl %eax, 16(%rsp)
movl %ebx, 20(%rsp)
movl %ecx, 24(%rsp)
movl %edx, 28(%rsp)
movl $0x80000004, %eax
cpuid
movl %eax, 32(%rsp)
movl %ebx, 36(%rsp)
movl %ecx, 40(%rsp)
movl %edx, 44(%rsp)
movl $s0, %edi
movq %rsp, %rsi
subl %eax, %eax
call printf
subl %eax, %eax
movq %rbp, %rsp
popq %rbp
ret
EAX=80000005h: 1차 캐시 및 TLB ID
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=80000006h: 확장 2차 캐시 기능
편집ECX의 2차 캐시의 자세한 내용을 반환한다. 캐시 크기와 코드를 표현하는 방법이 두 가지 있다.
.section .data
s0: .string "2차 캐시: %iMB\n"
.text
.global main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $0x80000006, %eax
cpuid
subl %edx, %edx
movl %ecx, (%rsp)
movl 2(%rsp), %eax
movl $1024, %ecx
divl %ecx
movl $s0, %edi
movl %eax, %esi
subl %eax, %eax
call printf
subl %eax, %eax
movq %rbp, %rsp
popq %rbp
ret
EAX=80000007h: 고급 전원 관리 정보
편집이 문단은 비어 있습니다. 내용을 추가해 주세요. |
EAX=80000008h: 선형 및 물리 주소 크기
편집해당 정보는 EAX 레지스터에 반환된다.
- 7:0 - 프로세서가 지원하는 물리적 주소 크기(MAXPHYADDR 이라고 하며 최대 52)
- 15:8 - 프로세서가 지원하는 선형 주소 너비
- 31:16 - 예약된 영역
다른 언어로부터 ID 접근
편집이 정보는 다른 언어로부터 접근하기 쉽다. 이를테면 아래의 C++ (gcc) 코드는 cpuid가 반환하는 처음 다섯 개의 값을 인쇄한다.
#include <iostream>
int main(int argc, char **argv) {
int b;
for (int a = 0; a < 5; a++) {
asm ( "mov %1, %%eax; " // a → eax
"cpuid;"
"mov %%eax, %0;" // eeax → b
:"=r"(b) /* 출력 */
:"r"(a) /* 입력 */
:"%eax" /* 레지스터 표시 */
);
std::cout << "코드 " << a << "은(는) 다음을 제공한다: " << b << std::endl;
}
return 0;
}
마이크로소프트 비주얼 C 컴파일러는 함수 __cpuid()를 내장하고 있으므로 cpuid 명령은 인라인 어셈블리를 사용하지 않아도 추가할 수 있다. x64 버전의 MSVC가 인라인 어셈블리를 아예 허용하지 않으므로 다루기 쉽다. MSVC의 경우 다음과 같이 하면 된다:
#include <iostream>
#include <intrin.h>
int main(int argc, char **argv) {
int b[4];
for (int a = 0; a < 5; a++) {
__cpuid(b,a);
std::cout << "코드 " << a << "은(는) 다음을 제공한다: " << b[0] << std::endl;
}
return 0;
}
각주
편집- ↑ http://www.intel.com/design/processor/manuals/253668.pdf
- ↑ Detecting Intel Processors - Knowing the generation of a system CPU
- ↑ “LXR linux-old/arch/i386/kernel/head.S”. 2012년 7월 13일에 원본 문서에서 보존된 문서. 2010년 6월 6일에 확인함.
같이 보기
편집외부 링크
편집- (영어) IA-32 구조 CPUID
- CPUID 안내: