CPUID

**CPUID** opcode는 x86 아키텍처를 위한 프로세서 기계 명령어이다. (CPUID는 CPU IDentification에서 비롯한다.) 인텔이 펜티엄과 SL 강화 486 프로세서를 내세운 1993년에 도입하였다.http://www.intel.com/design/processor/manuals/253668.pdf CPUID opcode를 사용하여 소프트웨어는 프로세서 종류와 MMX/SSE와 같은 기능들을 결정할 수 있다. CPUID opcode는 0FA2h이며 EAX 레지스터 값은 어떠한 정보를 반환할지를 결정한다. CPUID 명령을 일반적으로 사용하기까지 프로그래머들은 프로세서 메이커와 모델을 결정하기 위하여 CPU 동작에 사소한 차이를 불러일으킬 수 있는 비밀스러운 기계어를 기록하여야 했다.Detecting Intel Processors - Knowing the generation of a system CPUurl-status=dead
# 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 #include 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 >= (8 * i); printf("%c",(char)temp); } for(int i = 0; i >= (8 * i); printf("%c",(char)temp); } for(int i = 0; i >= (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 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 #include 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; }