Использование инструкции cpuid

Аннотация

Целью второй лабораторной работы является исследование возможностей инструкции cpuid, моделирование ветвлений, использование битовой логики, а также использование ассемблерных вставок в коде программ на языке высокого уровня.

Ход работы

  1. Перейдите в каталог для файлов лабораторных работ по курсу "Архитектура современных ЭВМ".
  2. Создайте каталог для файлов данной лабораторной работы.
  3. Скопируйте текст программы в новый файл testcpuid.S.
  4. /**
     * cpuid.S -- печатает сведения о подсистемах процессора
     *
     * Copyright (c) 2022 Petrozavodsk State University
     *
     * Permission is hereby granted, free of charge, to any person obtaining
     * a copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to
     * permit persons to whom the Software is furnished to do so, subject to
     * the following conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     */
    
    /* Секция данных */
    
    .data
    
    errmess:
            .string "CPUID is not supported\n"
    
    /* Секция команд процессора */
    
    .text
    
        .global _start                      # точка входа - глобальная метка
        _start:
    
        /* Подготавливаем стек к работе с локальными переменными */
            pushl %ebp                      # сохраняем текущий контекст стека
            movl  %esp, %ebp
            subl  $16, %esp                 # резервируем 16 байт для локального
                                            # буфера
    
        /* Анализируем, доступна ли инструкция cpuid */
            pushfl                          # получаем содержимое регистра флагов
            popl %eax                       # в регистре eax
            movl %eax, %ebx                 # сохраняем копию регистра флагов
            xorl $0x00200000, %eax          # меняем бит 21 на противоположный
            pushl %eax                      # сохраняем измененное значение
            popfl                           # в регистре флагов
            pushfl                          # снова получаем регистр флагов
            popl %eax
            xorl %ebx, %eax                 # если бит не сохранился
            je no_cpuid                     # cpuid не поддерживается
    
        /* Получим идентификатор производителя процессора */
            movl $0, %eax                  # загружаем код функции регистр eax
            cpuid                          # получаем строку производителя
            movl %ebx, -16(%ebp)           # в регистрах ebx, edx, ecx
            movl %edx, -12(%ebp)           # сохранаем в буфер
            movl %ecx, -8(%ebp)
            movb $10, -4(%ebp)             # помещаем символ перевода строки в
                                           # в конец буфера
    
        /* Выводим идентификационную строку производителя ЦП на экран */
            movl $4, %eax                  # помещаем номер системного вызова
                                           # write в регистр eax
            movl $1, %ebx                  # помещаем номер стандартного потока 
                                           # вывода (stdout) в ebx
            movl %ebp, %ecx                # помещаем адрес начала буфера выводе
            subl $16, %ecx                 # в ecx
            movl $13, %edx                 # помещаем длину строки в edx
            int $0x80                      # обращаемся к ОС для выполнения
                                           # системного вызова (вывода строки)
    
        /*** Дополнительные проверки - РЕАЛИЗУЙТЕ САМОСТОЯТЕЛЬНО ПО ЗАДАНИЮ */
    
    
        /*** */
    
            jmp done
    
        /* Обработка ситуации, если cpuid отсутствует (80386) */
        no_cpuid:
            movl $4, %eax                  # помещаем номер системного вызова
                                           # write в регистр eax
            movl $1, %ebx                  # помещаем номер стандартного потока 
                                           # вывода (stdout) в ebx
            leal errmess, %ecx             # помещаем адрес начала буфера вывода
            movl $24, %edx                 # помещаем длину строки в edx
            int $0x80                      # обращаемся к ОС для выполнения
                                           # системного вызова (вывода строки)
    
        /* Организуем корректное завершение программы */
        done:
            mov %ebp, %esp                  # восстанавливаем контекст стека
            popl %ebp
    
            movl $1, %eax                   # загружаем в eax номер вызова exit
            movl $0, %ebx                   # загружаем в ebx код возврата 0
            int $0x80                       # выполняем обращение к ОС для
                                            # выполнения системного вызова
    
    .end                                    # последняя строка исходного текста  
    
  5. Выполните ассемблирование и сборку программы.
  6. Запустите программу, убедитесь, что на экране напечатан идентификатор производителя процессора (GenuineIntel, AuthenticAMD или иной).
  7. Изучите реализацию механизма проверки наличия инструкции cpuid, которая основана на возможности изменения на противоположное значение 21 бита регистра флагов:
    1. через стек (так как инструкции прямого копирования регистра флагов в другой регистр не предусмотрено) копируем флаги в EAX;
    2. создаем копию исходного значения регистра флагов в регистре EBX;
    3. меняем 21 бит в EAX на противоположный;
    4. возвращаем модифицированное значение EAX на регистр флагов;
    5. снова вытаскиваем флаги в EAX;
    6. сравниваем с сохраненной в EBX копией;
    7. если они не отличаются, переходим на фрагмент кода, печатающий сообщение о недоступности cpuid.
  8. Изучите реализацию механизма получения идентификатора производителя процессора:
    1. в EAX устанавливаем номер функции cpuid для получения данных о производителе (функция 0);
    2. выполняем инструкцию cpuid;
    3. 12-байтовый идентификатор получаем в регистрах EBX, EDX, ECX;
    4. в EAX получаем максимально допустимый номер поддерживаемой данным процессором функции cpuid.
  9. Реализуйте проверку доступности фукнции 1 инструкции cpuid:
    1. проанализируйте значение регистра EAX, полученное в результате выполнения функции 0 инструкции cpuid (см. шаг 7.4);
    2. если функция 1 недоступна, программа должна выводить диагностическое сообщение и корректно завершаться;
    3. убедитесь, что при недоступности функции 1 инструкции cpuid программа отрабатывает корректно, для этого, выполняя программу в отладчике, вручную обнулите (подмените) значение регистра EAX перед проверкой доступности.
  10. Реализуйте проверку наличия математического сопроцессора (x87 FPU on Chip) и подсистемы MMX с помощью cpuid:
    1. в EAX устанавливаем номер функции для получения feature information процессора (функция 1);
    2. вызываем cpuid;
    3. убеждаемся, что нужный бит регистра EDX (туда будет записана информация о наличии и отсутствии подсистем) равен 1. Для FPU это бит 0, для MMX - 23. Для тестирования битов самостоятельно изучите команду test, а также использование команды je в связке с арифметической или логической инструкцией (пример использования je имеется в данной программе).
  11. Реализуйте небольшую статическую библиотеку на языке программирования C для проверки наличия математического сопроцессора и программу для проверки функций библиотеки. Функции библиотеки (может быть, единственная функция) должны использовать ассемблерные вставки для вызова инструкции cpuid и получения результата проверки.