Rozpoznawanie typu procesora


(przeskocz wykrywanie procesora)

Jak zapewne wiecie, wiele programów (systemy operacyjne, gry, ...) potrafi jakoś dowiedzieć się, na jakim procesorze zostały uruchomione. Rozpoznanie typu procesora umożliwia na przykład uruchomienie dodatkowych optymalizacji w programie lub odmowę dalszego działania, jeśli program musi korzystać z instrukcji niedostępnych na danym procesorze.
Wykrywanie rodzaju CPU i FPU nie jest trudne i pokażę teraz, jak po kolei sprawdzać typ procesora (nie można przecież zacząć sprawdzania od najwyższych).
Informacje, które tutaj podam, są oczywiście słuszne dla wszystkich procesorów rodziny x86 (AMD, Cyrix, ...), a nie tylko Intela.

Generalnie sposób wykrywania pod Linuksem jest jeden (nie wliczając żadnych dodatkowych funkcji jądra czy też czytania z /proc/cpuinfo): poprzez rejestr E-FLAG.

  1. 80386
    (przeskocz 80386)

    Na tym procesorze nie można zmienić bitu numer 18 we flagach (wiemy, że rejestr flag ma 32 bity). Bit ten odpowiada za Alignment Check i spowoduje przerwanie m.in wtedy, gdy SP nie będzie podzielne przez 4. Dlatego, zanim będziemy testować ten bit, musimy zachować SP i wyzerować jego najmłodsze 2 bity.


    (przeskocz kod dla 80386)
    		mov	dx, sp
    		and	sp, ~3		; aby uniknąć AC fault.
    					; FASM: and sp, not 3
    		pushfd			; flagi na stos
    		pop	eax		; EAX = E-flagi
    		mov	ecx, eax	; zachowanie EAX
    		xor	eax, 40000h	; zmiana bitu 18
    		push	eax		; EAX na stos
    		popfd			; E-flagi = EAX
    		pushfd			; flagi na stos
    		pop	eax		; EAX = flagi
    		xor	eax, ecx  ; czy takie same? jeśli tak, to 386
    		mov	sp, dx		; przywrócenie SP
    		jz	jest_386
  2. 80486
    (przeskocz 80486)

    Na tym procesorze nie można zmienić bitu 21 we flagach. Jeśli ten bit można zmienić, to procesor obsługuje instrukcję CPUID, której będziemy używać do dalszego rozpoznania. Kod:


    (przeskocz kod dla 80486)
    		pushfd			; flagi na stos
    		pop	eax		; EAX = E-flagi
    		mov	ecx, eax	; zachowanie EAX
    		xor	eax, 200000h	; zmiana bitu 21
    		push	eax		; EAX na stos
    		popfd			; E-flagi = EAX
    		pushfd			; flagi na stos
    		pop	eax		; EAX = flagi
    		xor	eax, ecx  ; czy takie same? jeśli tak, to 486
    		jz	jest_486
    		jmp	jest_586

Zanim omówię sposób korzystania z instrukcji CPUID, zajmijmy się sposobem rozpoznania typu koprocesora.



Koprocesor


(przeskocz wykrywanie koprocesora)

Tutaj możliwości są tylko 4: brak koprocesora, 8087, 80287, 80387. No to do roboty.

  1. czy w ogóle jest jakiś koprocesor?
    (przeskocz test na istnienie FPU)

    To sprawdzamy bardzo łatwo. Jeśli nie ma koprocesora, to w chwili wykonania instrukcji FPU może wystąpić przerwanie INT6 (nieprawidłowa instrukcja), ale nie o tym sposobie chciałem powiedzieć. Koprocesor można wykryć, jeśli słowo stanu zostanie zapisane prawidłowo. Oto kod:


    (przeskocz test na istnienie FPU)
    		fninit			; inicjalizacja zeruje rejestry
    
      		; wpisujemy jakąś niezerowa wartość:
      		mov	word [_fpu_status], 5a5ah
    
    		; zapisz słowo statusowe do pamięci:
    		fnstsw	[_fpu_status]
    		mov	ax, [_fpu_status]
    		or	al, al	; jeśli zapisało dobrze (zera oznaczają
    				; puste rejestry), to jest FPU
    
    		jz	jest_FPU
  2. 8087
    (przeskocz 8087)

    Sztuczka polega na wykorzystaniu instrukcji FDISI (wyłączenie przerwań), która rzeczywiście coś robi tylko na 8087. Po wyłączeniu przerwań w słowie kontrolnym zostaje włączony bit numer 7.


    (przeskocz kod dla 8087)
    		; zachowaj słowo kontrolne do pamięci:
    		fnstcw	[_fpu_status]
    
    		; wyłączamy wszystkie
    		; przerwania (poprzez słowo kontrolne):
    		and	word [_fpu_status], 0ff7fh
    
    		; załaduj słowo kontrolne z pamięci:
    		fldcw	[_fpu_status]
    
    		fdisi		; wyłączamy wszystkie przerwania
    				; (jako instrukcja)
    
    		; zachowaj słowo kontrolne do pamięci:
    		fstcw	[_fpu_status]
    		test	byte [_fpu_status], 80h	; bit 7 ustawiony?
    
    		jz	nie_8087	; jeśli nie, to nie jest to 8087
  3. 80287
    (przeskocz 80287)

    Koprocesor ten nie odróżnia minus nieskończoności od plus nieskończoności. Kod na sprawdzenie tego wygląda tak:


    (przeskocz kod dla 80287)
    		finit
    
    		fld1		; st(0)=1
    		fldz		; st(0)=0,st(1)=1
    		fdivp	st1	; tworzymy nieskończoność,
    				; dzieląc przez 0
    		fld	st0	; st(1):=st(0)=nieskończoność
    		fchs		; st(0)= minus nieskończoność
    
    				; porównanie st0 z st1 i
    				; zdjęcie obu ze stosu
    		fcompp		; 8087/287: -niesk. = +niesk.,
    				; 387: -niesk. != +niesk.
    
    		fstsw	[_fpu_status]	; zapisz status do pamięci
    		mov	ax, [_fpu_status]	; AX = status
    
    		sahf		; zapisz AH we flagach. tak sie składa,
    				; że tutaj również flaga ZF wskazuje na
    				; równość argumentów.
    		jz	jest_287
    		jmp	jest_387


Dalsze informacje o procesorze - instrukcja CPUID

Od procesorów 586 (choć niektóre 486 też podobno ją obsługiwały), Intel i inni wprowadzili instrukcję CPUID. Pozwala ona odczytać wiele różnych informacji o procesorze (konkretny typ, rozmiary pamięci podręcznych, dodatkowe rozszerzenia, ...).
Korzystanie z tej instrukcji jest bardzo proste: do EAX wpisujemy numer (0-3) i wywołujemy instrukcję, na przykład

	mov	eax, 1
	cpuid

Teraz omówię, co można dostać przy różnych wartościach EAX.

  1. EAX=0
    (przeskocz EAX=0)

    EAX = maksymalny numer funkcji dla CPUID.
    EBX:EDX:ECX = marka procesora (12 znaków ASCII).
    Intel - GenuineIntel
    AMD - AuthenticAMD
    NexGen - NexGenDriven
    Cyrix, VIA - CyrixInstead
    RISE - RiseRiseRise,
    Centaur Technology/IDT - CentaurHauls (programowalne, może być inne)
    United Microelectronics Corporation - UMC UMC UMC
    Transmeta Corporation - GenuineTMx86
    SiS - SiS SiS SiS
    National Semiconductor - Geode by NSC.

  2. EAX=1
    (przeskocz EAX=1)

    EAX = informacje o wersji:

    EDX = cechy procesora (tutaj akurat z procesorów Intela; najpierw numery bitów):


  3. EAX=2

    EBX, ECX, EDX = informacje o pamięci podręcznej cache i TLB

Nawet te informacje, które tu przedstawiłem są już bardzo szczegółowe i z pewnością nie będą takie same na wszystkich procesorach. To jest tylko wstęp. Dalsze informacje można znaleźć na stronach producentów procesorów, na przykład AMD, Intel, ale także tutaj: Sandpile, Lista przerwań Ralfa Brown'a (plik opcodes.lst).


Spis treści off-line (Alt+1)
Spis treści on-line (Alt+2)
Ułatwienia dla niepełnosprawnych (Alt+0)