50

Я знаю, что BIOS загружает свою первую инструкцию из 0xFFFFFFF0, но почему этот конкретный адрес? У меня есть куча вопросов и, надеюсь, вы сможете мне помочь с некоторыми из них, по крайней мере.

Мои вопросы:

  • Почему первая инструкция BIOS находится в верхней части 4 ГБ ОЗУ?
  • Что произойдет, если мой компьютер имеет только 1 ГБ оперативной памяти?
  • Как насчет систем с более чем 4 ГБ ОЗУ (например, 8 ГБ, 16 ГБ и т.д.)?
  • Почему стек инициализируется некоторым значением (в данном случае значением, расположенным в 0xFFFFFFF0)?

Я читал об этом сегодня днем, и до сих пор не понимаю.

7 ответов7

55

0xFFFFFFF0 - это то место, где x86-совместимый процессор начинает выполнять инструкции при включении. Это аппаратный, неизменяемый (без дополнительного оборудования) аспект ЦП, и разные типы ЦП ведут себя по-разному.

Почему первая инструкция BIOS находится в верхней части 4 ГБ ОЗУ?

Он расположен в верхней части 4 ГБ адресного пространства - и при включении BIOS или ПЗУ UEFI настроено реагировать на чтение этих адресов.

Моя теория о том, почему это так:

Почти все в программировании лучше работает с непрерывными адресами. Разработчик ЦП не знает, что системный сборщик захочет делать с ЦП, поэтому для ЦП плохая идея требовать, чтобы адреса попали в середину пространства, необходимого для различных целей. Лучше держать это "в стороне" вверху или внизу адресного пространства. Конечно, имейте в виду, что это решение было принято, когда 8086 был новым, у которого не было MMU.

В 8086 году векторы прерывания существовали в ячейке памяти 0 и выше. Векторы прерываний должны располагаться по известным адресам, и было желательно, чтобы они были в оперативной памяти для обеспечения гибкости, однако разработчик ЦП не мог знать, сколько ОЗУ будет в системе. Так что начинать с 0 и работать имело смысл для них (потому что ни одна система в 1978 году, когда был изобретен 8086, не имела бы 4 Гбайт ОЗУ - поэтому ожидать, что ОЗУ будет иметь значение 0xFFFFFFF0, было не очень хорошей идеей), и тогда ПЗУ должно было быть на верхней границе.

Конечно, начиная, по крайней мере, с 80286, векторы прерываний можно было бы переместить в другое начальное местоположение, отличное от 0, но современные 64-битные процессоры x86 по-прежнему загружаются в режиме 8086, поэтому для совместимости все по-прежнему работает по-старому (как это ни смешно) как это звучит в 2015 году, вам все еще нужен ваш процессор x86 для запуска DOS).

Таким образом, поскольку векторы прерываний начинаются с 0 и работают вверх, ПЗУ должно начинаться сверху и работать вниз.

Что произойдет, если на моем компьютере будет только 1 ГБ ОЗУ?

32-разрядный ЦП имеет 4 294 967 296 адресов, пронумерованных от 0 (0x00000000) до 4294967295 (0xFFFFFFFF). ROM может жить по одним адресам, а RAM - по другим. С MMU процессора это можно даже переключать на лету. Оперативная память не должна жить по всем адресам.

При наличии только 1 ГБ ОЗУ некоторые адреса не будут отвечать на запросы при чтении или записи. Это может привести к чтению неверных данных при обращении к таким адресам или к блокировке системы.

А как насчет систем с объемом оперативной памяти более 4 ГБ (например, 8 ГБ, 16 ГБ и т.д.)?

Сохраняя это несколько простым: например, 64-разрядные процессоры имеют больше адресов (что делает их 64-разрядными, например, от 0x0000000000000000 до 0xFFFFFFFFFFFFFFFF), поэтому дополнительная оперативная память "подходит". Предполагая, что процессор находится в длинном режиме. До этого ОЗУ есть, просто не адресуется.

Почему стек инициализируется некоторым значением (в этом случае значением, расположенным в 0xFFFFFFF0)?

Я не могу сразу найти что-то о том, что x86 назначает указатель стека при включении питания, но в любом случае он должен был бы быть переназначен подпрограммой инициализации, как только эта подпрограмма узнает, сколько ОЗУ находится в системе. (@Eric Towers в комментариях ниже сообщает, что при включении питания он равен нулю.)

25

Он не расположен в верхней части оперативной памяти; он находится в ПЗУ, адрес которого находится в верхней части адресного пространства памяти, вместе с любой памятью на платах расширения, например, контроллерами Ethernet. Он существует для того, чтобы он не конфликтовал с оперативной памятью, по крайней мере, до тех пор, пока не будет установлено 4 ГБ. Системы, имеющие 4 ГБ или более ОЗУ, могут разрешить конфликт двумя способами. Дешевые материнские платы просто игнорируют части оперативной памяти, которые конфликтуют с местом расположения ПЗУ. Достойные перераспределяют эту оперативную память так, чтобы она имела адрес выше отметки 4 ГБ.

Я не уверен, что вы спрашиваете о стеке. Это конечно не инициализировано, чтобы быть в ROM. Когда процессор перезагружается, он изначально находится в "реальном режиме", где он работает так же, как и исходный 8086, и использует 16-разрядную сегментированную адресацию, позволяя ему получить доступ только к 1 МБ памяти. Код BIOS расположен в верхней части этого 1 МБ. BIOS выбирает где-то в ОЗУ для установки стека, загружает и выполняет первый сектор первого загрузочного диска. Операционная система должна переключиться в 32- или 64-битный режим, как только он вступит во владение, и настроит свои собственные стеки (по одному на задачу / поток).

12

Во-первых, это не имеет ничего общего с оперативной памятью. Мы говорим здесь об адресном пространстве - даже если у вас есть только 16 МБ памяти, у вас все еще есть 32-битное адресное пространство на 32-битном процессоре.

Это уже отвечает на ваш первый вопрос, на самом деле - в то время, когда это было спроектировано, на ПК реального мира не было почти полных 4 ГБ памяти; их было больше в диапазоне 1-16 мегабайт памяти. Адресное пространство было, по сути, свободным.

Теперь, почему именно 0xFFFFFFF0? Процессор не знает, сколько там BIOS. Некоторые BIOS могут занимать всего несколько килобайт, в то время как другие могут занимать полные мегабайты памяти - и я даже не вхожу в различные дополнительные RAM. Процессор должен быть жестко привязан к некоторому адресу, чтобы начать - нет необходимости настраивать процессор. Но это только сопоставление адресного пространства - адрес отображается непосредственно в микросхему ПЗУ BIOS (да, это означает, что вы не получите доступ к полному 4 ГБ ОЗУ в этот момент, если у вас есть так много - но в этом нет ничего особенного, многим устройствам требуется свой диапазон адресов). На 32-разрядном процессоре этот адрес дает вам полные 16 байтов для выполнения базовой инициализации - этого достаточно для настройки ваших сегментов и, при необходимости, адресного режима (помните, x86 загружается в 16-разрядном реальном режиме - адресное пространство) не плоская) и сделайте переход к настоящей процедуре загрузки. На данный момент вы вообще не используете ОЗУ - все это просто ПЗУ. Фактически, ОЗУ даже не готово к использованию на этом этапе - это одна из задач BIOS POST! Теперь вы можете подумать - как 16-битный реальный режим получает доступ к адресу 0xFFFFFFF0? Конечно, есть сегменты, поэтому у вас есть 20-битное адресное пространство, но этого все еще недостаточно. Что ж, есть хитрость: 12 старших бит адреса устанавливаются до тех пор, пока вы не выполните свой первый длинный прыжок, предоставляя вам доступ к высокому адресному пространству (при этом отказывая в доступе к чему-либо ниже 0xFFF00000 - пока вы не выполните длинный прыжок) ,

Все это то, что в основном скрыто от программистов (не говоря уже о пользователях) в современных операционных системах. Обычно у вас нет доступа к чему-либо настолько низкому уровню - некоторые вещи уже не поддаются спасению (вы не можете переключать режимы процессора вольно или невольно), некоторые обрабатываются исключительно ядром ОС.

Таким образом, более хороший взгляд приходит из старой школы кодирования на MS DOS. Другой типичный пример того, как память устройства напрямую сопоставляется с адресным пространством, - это прямой доступ к видеопамяти. Например, если вы хотели быстро записать текст на дисплей, вы написали непосредственно по адресу B800:0000 (плюс смещение - в текстовом режиме 80x25, это означало (y * 80 + x) * 2 если память мне не изменяет - два байт на символ, строка за строкой). Если вы хотите рисовать попиксельно, вы использовали графический режим и начальный адрес A000:0000 (обычно 320x200 при 8 битах на пиксель). Делать что-то высокопроизводительное обычно означало погрузиться в руководства по устройствам, чтобы выяснить, как получить к ним прямой доступ.

Это сохранилось до наших дней - оно просто скрыто. В Windows вы можете видеть адреса памяти, сопоставленные с устройствами, в диспетчере устройств - просто откройте свойства чего-то вроде вашей сетевой карты, перейдите на вкладку Ресурсы - все элементы диапазона памяти являются отображениями из памяти устройства в ваше основное адресное пространство. А в 32-разрядной системе вы увидите, что большинство этих устройств отображаются выше отметки 2 ГиБ (позднее 3 ГиБ) - опять же, чтобы минимизировать конфликты с используемой пользователем памятью, хотя на самом деле это не проблема виртуальной памяти ( приложения не приближаются к реальному аппаратному адресному пространству - у них есть собственный виртуализированный кусок памяти, который может быть сопоставлен, например, с ОЗУ, ПЗУ, устройствами или файлом подкачки).

Что касается стека, ну, это должно помочь понять, что по умолчанию стек растет сверху. Так что если вы сделаете push новый указатель стека будет 0xFFFFFEC - другими словами, вы не пытаетесь записать в BIOS инициализации адрес :) Что, конечно , означает , что BIOS инициализации процедуры можно использовать стек безопасно до переназначить его куда-нибудь полезнее. В программировании старой школы до того, как пейджинг стал де-факто по умолчанию, стек обычно начинался с конца ОЗУ, и "переполнение стека" происходило, когда вы начинали перезаписывать память приложения. Защита памяти во многом изменилась, но в целом она поддерживает обратную совместимость в максимально возможной степени - обратите внимание, как даже самый современный процессор x86-64 все еще может загружать MS DOS 5 - или как Windows все еще может запускать многие приложения DOS, которые не имеют Идея о пейджинге.

6

В дополнении к другим точкам упомянутых, это может быть полезно , чтобы понять , что такой адрес. В то время как новые архитектуры усложняют ситуацию, исторически машина в каждом цикле памяти выводила желаемый адрес на 20–32 провода (в зависимости от архитектуры, с некоторыми специальными приемами, чтобы указать, нужно ли ей пару или четыре байта одновременно); Различные части системы памяти будут проверять состояние этих проводов и активировать себя, когда они увидят определенные комбинации высоких и низких значений.

Если машине с 32 адресными проводами нужно было использовать только 1 МБ ОЗУ и 64 КБ ПЗУ (что вполне допустимо для некоторых встроенных контроллеров), она может активировать ОЗУ для всех адресов, где провод верхнего адреса был низким, и ПЗУ для всех адресов, где она была. высоко. Нижние 20 адресных проводов затем будут привязаны к ОЗУ для выбора одного из 1048 576 байт, а нижние 16 будут также подключены к ПЗУ, чтобы выбрать один из 65 536 байтов. Остальные 11 адресных проводов просто не будут ни к чему подключены.

На такой машине доступ к адресам 0x00100000-0x001FFFFF будет эквивалентен доступу к адресам ОЗУ 0x00000000-0x000FFFFF. Аналогично с адресами 0x000200000-0x0002FFFFF или 0x7FF00000-0x7FFFFFFFF. Все адреса выше 0x80000000 будут считывать ПЗУ, с шаблоном 64K, повторяющимся во всем пространстве.

Несмотря на то, что процессор имеет адресное пространство 4 294 967 296 байт, аппаратному обеспечению не нужно распознавать такое количество разных адресов. Размещение вектора сброса вблизи верхней части адресного пространства - это проект, который будет хорошо работать независимо от того, сколько или мало ОЗУ и ПЗУ в системе, и избегает необходимости полностью декодировать адресное пространство.

3

Моя теория заключается в том, что мы используем отрицательную логику, цифровая единица (1) не имеет напряжения вообще (O вольт). Нам нужно только наложить напряжение на последние 4 бита при инициализации, чтобы счетчик программ (или указатель инструкций) установился на 1111 1111 1111 1111 1111 1111 1111 0000. Нам не нужно обращаться к старшим 28 битам, так как большинство (старых) процессоров) были 16 битами, а младшие кусочки могут быть адресованы одним адресным чипом в старые времена. Теперь, поскольку у нас есть 64 бита с совместимостью с 32 битами и 32 битами с 16 битами, аппаратное обеспечение было улучшено, но метод остается. Также биозы не всегда запрограммированы на 64 или 32 бита. Мое мнение так же, поскольку воспоминания не всегда одинаковы, биос должен быть расположен в том же первом сегменте. То, как мы видим адреса биосов, не всегда является реальным адресом. Просто учил меня ...

2

после сброса процессор, совместимый с 8088/8086, выполняет команды в 0FFFF0, что на 16 байтов ниже предела в 1 мегабайт. обычно ПЗУ в этом месте (в реализациях на ПК) будет представлять собой BIOS, поэтому в конце ПЗУ BIOS происходит переход к началу BIOS.

показано здесь: начальный вектор и подпись «дата» за ним, IBM 5150 PC 8KB eprom dump bios дата: 19.10.1981

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

обратите внимание, что адресация составляет 8 КБ за 2000 $, что помещает начальный адрес (абсолютный дальний JMP, в любое другое место, в данном случае внутри самого 8 КБ, хотя не самый низкий возможный адрес в этом) в $ FFFF:$ 0 сегментировано или $ FFFF0 линейно.

Что касается совместимости: если какой-то «будущий» или текущий процессор «ожидает», что он будет иметь намного больше F перед адресом, это не имеет значения. для совместимости более новых процессоров в более старых системах дополнительные адресные линии остаются не связанными, и поэтому данные в базе данных точно такие же. до тех пор, пока младшие значащие биты остаются FFFF0.

(в системе с только 1 Мб оперативной памяти и ромом, расположенным в конце этого оперативной памяти, и ничего больше, он с радостью «подумает», что он говорит по старшему адресу, но получит те же самые данные, потому что эти реализации никогда не слышали о адресные строки выше чем А19)

обратите внимание, что мир - это не просто «ПК» ... ПК IBM был «несчастным случаем», эти процессоры никогда не были специально разработаны для «ПК» и включают в себя гораздо больше вещей, чем просто ПК (таких как сателлиты, оружейные системы и т. д.). 32- и 64-битный защищенный режим обычно нежелателен. (Виртуальный режим 8086 гораздо интереснее, например, причина выбора более новой (386+) версии). поэтому «обратная совместимость» - это гораздо больше, чем просто «будет ли он работать с DOS».

1

Материнская плата гарантирует, что инструкция в векторе сброса является переходом в область памяти, сопоставленную с точкой входа BIOS. Этот переход неявно очищает скрытый базовый адрес, присутствующий при включении питания. Все эти области памяти имеют правильное содержимое, необходимое для процессора, благодаря карте памяти, хранящейся в чипсете. Все они сопоставлены с флэш-памятью, содержащей BIOS, так как в этот момент модули RAM имеют случайную чепуху.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .