При включении все процессоры Intel работают в реальном режиме адресации. Реальная адресация - это режим, совместимый с ранними Intel процессорами. По мере усложнения процессоров наследуемый код всегда использовался в новых процессорах для совместимости. В реальном режиме адресации процессор может выполнять программы, написанные для 8086 и 8088 с использованием тех же самых инструкций и, что гораздо важнее, с помощью того же метода адресации или преобразования адресов (address translation). В результате преобразования адресов процессор получает доступ к системной памяти. Ранние процессоры Intel имели 20-битовую адресную шину, через которую они могли адресовать до 64 Кб памяти. Это ограничение накладывалось и на код ранних систем. В реальном режиме адресации линейные адреса равны физическим адресам. По мере нашего продвижения по коду инициализации менеджера памяти мы увидим и другие особенности последних процессоров, используемые в аппаратных и более комплексных структурах вдобавок к программным.
Код в setup. S выполняет несколько важных функций, связанных с инициализацией памяти.
*rch/i386/boot/setup.S
307 #define SMAP 0x534d4150
%ebx, %ebx # последовательный счетчик $Е820МАР, %di # указатель в списке записи # так что мы можем иметь прямую запись в bios.
308
309 meme820:
jmpe820: movl $0x0000e820, movl $SMAP, %edx movl $20, %ecx
%eax # e82 0, верхнее слово обнулено
# ascii 'SMAP' # размер e820rec
# запись данных.
pushw %ds popw %es int $0x15 jc bail820
# вызов
# сброс е801.
если произошла неудача
%еах # проверка, возврата из 'SMAP' # сброс в е801 если произошла неудача
310 xorl
311 mow 312 314 315 316 317 318 319 320 321 322 323
Строки 307—345
Если посмотреть на этот блок кода, мы сначала увидим (в строке 321) вызов функции BIOS intl5h с ах= 0хе820. Она возвращает адрес и длину различных типов памяти, распознанных BIOS. Эта простая карта памяти представляет простой пул, из которого выбираются все страницы памяти в Linux. Как видно из рассмотренного
ранее кода, карту памяти можно получить тремя способами: 0хе82 0, 0x801 и 0x88. Все эти три метода совместимы с существующими BIOS и соответствующими платформами.
Строки 595-628
Этот код представляет собой образ ядра, созданный build. с и загруженный LILO. Он выполняется для сектора init (по адресу 0x9000), сектора setup (по адресу 0x9200) и сжатого образа. Образ изначально загружается по адресу 0x10000. Если он LARGE (>0x7FF), он остается на месте; в противном случае перемещается в 0x1000.
Формирование 20-битового физического адреса в режиме реальной адресации Intel
Процессор Intel 8088 в оригинальном IBM PC имел только 20 линий адресации [0...19]. Это позволяло системе адресовать до 1 Мб плюс приблизительно 64 Кб внутренней памяти (от 0 до 0xl0_FFEF), но физически (через шину) последние 64 Кб адресуемой памяти были на самом деле первыми 64 Кб реальной памяти!
Внутри процессора 20-битовый адрес формировался из 16-битового сегмента селектора и 16-битового сегмента отступа. Селектор сдвигался на 4 байта и добавлялся к отступу, расширяя его на 4 бита. Сумма этих регистров равнялась физическому адресу, видимому через шину.
Например, для получения высших адресов мы загружаем в сегмент селектора (CS, DS, ES и т. д.) значение OxFFFF, а значение OxFFFF в регистр индекса (SI, DI и т. д.). Внутри процессора сегмент селектора сдвигается на 4 бита и добавляется к отступу.
OxFFFF сдвигается на 4 6HTa=0x0FFFF0 Добавляется отступ+OxOOFFFF Внутренняя cyMMa=0xl0_FFEF Внешний физический aapec=0x00_FFEF
В результате физический адрес равен сегментному селектору со значением 0x0000 и отступу со значением OxFEFF (0000:FFEF).
Доступ к наивысшему адресу и выше спустит нас в OxFFEF. Некоторые написанные для этого процессора программы зависят от этого 20-битового циклического поведения. Представленные процессоры Intel 286 и более поздние с более широкими шинами адресов использовали реальную адресацию для сохранения совместимости с 8088 и 8086. Режим реальной адресации не учитывал потребностей старых, зависящих от циклического 20-битового эффекта программ. Была добавлена сигнальная дорожка А20М# для имитации этой «особенности» ранних процессоров. Получение этого сигнала маскировало сигнал А20 для доступа к нижней памяти.
Для включения и выключения сигнала А20 использовался логический вентиль. Оригинальный дизайн получения этого сигнала предусматривал использование дополнительного ввода-вывода от контроллера клавиатуры, управляемого портами ввода-вывода 0x60 и 0x64. Позднее был разработан «быстрый вентиль А20», использовавший порт 0x92, встроенный в материнскую плату. Так как все процессоры х86 сбрасываются в реальном режиме адресации, при загрузке имеет смысл включить режим А20 одним из двух доступных способов.
991 992 993 994 995 996 997 998 999
.word 0 # базовый адрес = 0
.word 0x9200 # чтение-запись данных
.word OxOOCF # гранулярность = 4096, 386
# (+5-Й полубайт предела) gdt_end: .align 4
# байт выравнивания
Строки 790-797
Структуры и данные для временных GDT и IDT компилируются в конце setup. S. Эти таблицы реализованы в своей простейшей форме.
Строки 981-1006
Эти строки являются откомпилированными значениями временного GDT, который имеет код и описатель данных, каждый из которых соответствуют 4 Гб памяти, начиная с 0x00. ЮТ инициализируется в 0x00 и заполняется позднее.
На этом этапе разворачивания менеджера памяти для платформы Intel одной из важнейших фаз является переход в защищенный режим. В этой точке аппаратура начинает построение пространства виртуальных адресов для операционной системы.
Защищенный режим
Метод управления памятью Intel называется защищенным режимом. Защита относится к множеству независимых сегментированных адресных пространств, защищенных друг от друга. Другой половиной менеджера памяти Intel являются страницы и их преобразование. Системные программисты могут использовать различные комбинации сегментации с разделением на страницы, однако Linux использует плоскую модель, где сегментация не учитывается. В плоской модели каждый процесс может адресовать полное 32-битовое пространство (4 Гб).
Строки 830-831
Установка бита РЕ в слове состояния машины для входа в защищенный режим. Инструкция j mp начинает выполнение в защищенном режиме.
Строки 834-839
Сохранение 32-битового указателя в защищенном режиме для распаковки и загрузки ядра позже в startup_32 ().
Вспомните, что в реальном режиме адресации код выполняется с помощью 16-битовых инструкций. Текущий файл компилируется с помощью ассемблерной директивы . codel6, включающей данный режим, также известный как 16-битовый модуль в Intel Programmer's Reference. Для перехода от 16-битового модуля к 32-битовому модулю архитектура Intel (и магия ассемблера) позволяет строить 32-битовые инструкции в 16-битовом модуле.
Построение и выполнение 32-битового перехода:
arch/i386/boot/setup.S
841 # переход к startup_32 в arch/i38б/kernel/head.S
842 #
843 # ПРИМЕЧАНИЕ. Для большой загрузки ядра в верхнюю память нужно
844 # jmpi 0x100000, BOOT_CS
846 # но мы еще не загрузили регистр CS, так что размер по умолчанию
847 # отступа задачи до сих пор 16-битовый.
848 # Однако при использовании префиксного операнда (0x66) процессор
849 # корректно получит наш 48-битовый указатель. (INTeL 80386
850 # Programmer's Reference Manual, Mixing 16-bit and 32-bit code,
851 # страница 16-6)
852 .byte 0x66, Oxea # prefix + jmpi-opcode
853 code32: .long 0x1000 # устанавливается в 0x100000
854 # для большого ядра
855 .word BOOT CS
Строка 852
Эта строка строит 32-битовую инструкцию перехода.
После выполнения перехода система использует временный GDT, а код выполняется в 32-битовом защищенном режиме начиная с метки startup_32 в arch/i386/kernel/ head. s, строка 57.