Почему первая инструкция BIOS расположена в 0xFFFFFFF0 («верх» ОЗУ)?

51

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

Мои вопросы:

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

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

Фернандо Паладини
источник
28
Один вопрос на вопрос, пожалуйста.
Легкость гонок с Моникой
4
Мне нравится, что в принятом ответе даже не упоминается сегментированная память или режимы адресации, и единственное место, где даже коснулась строка A20, - это комментарии.
Ималлетт
AVR Atmel запускаются с адреса 0, а Freescale HCS08 - с 0xFFFE, iirc. Каждое семейство процессоров имеет свои особенности.
Ник Т
2
@imallett Мне нравится, как вы решили жаловаться на это здесь, а не просить автора дополнить свой ответ дополнительной информацией. Мне также нравится то, как вы думаете, что это знание, которое ОП сможет определить, хотя цель вопроса - получить знания о вещах, которые теперь можно полностью понять.
MonkeyZeus
2
@MonkeyZeus на сегодняшний день, 9 других комментаторов уже сделали это, и это все еще не изменилось. Мой комментарий, хотя и саркастический, не был пустым; это предупреждение для будущих пользователей сети, а также для ОП.
Ималлетт

Ответы:

57

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 в комментариях ниже сообщает, что при включении питания он равен нулю.)

LawrenceC
источник
7
Лучше всего рассматривать адресное пространство как большое пространство, в котором вещи могут быть назначены аппаратно. Когда процессор читает / записывает память, он фактически осуществляет связь по шине, и аппаратное обеспечение может гарантировать, что такие вещи, как ОЗУ или ПЗУ, реагируют на определенные диапазоны адресов. Таким образом, такое оборудование должно убедиться, что ПЗУ реагирует на 0xFFFFFFF0 при перезагрузке ЦП. Не существует неотъемлемого обязательства, что ПЗУ появляется сразу после ОЗУ. Оно может появиться везде, где об этом говорит аппаратное обеспечение, в зависимости от возможностей такого оборудования.
LawrenceC
4
Можно иметь «дыры» или неназначенные пробелы, которые не используются ПЗУ, ОЗУ или чем-либо еще - обычно доступ к ним приводит к блокировке системы.
LawrenceC
16
Этот ответ предполагает, что ЦП может использовать 32 адресных бита в 16-битном режиме. Но в 16-битном режиме он может использовать только 20 адресных бит. Адрес 0xFFFFFFF0недоступен до тех пор, пока ЦП не будет переключен в 32-битный режим. В прошлый раз, когда я внимательно изучал код BIOS, точка входа была на 0xFFFF0.
Касперд
6
@ MichaelKjörling Ваш расчет неверен. Сдвинутый сегмент и смещение не OR, они добавляются. Таким образом, логический FFFF: FFF0 является физическим (1) 0FFE0 (где ведущий 1 присутствует, если включен A20).
Руслан
9
@kasperd Хак на месте - у менеджера памяти старшие 12 битов установлены в 1, пока не произойдет первый длинный прыжок. Так что, по логике вещей, вы работаете 0xFFFF0, но на самом деле это соответствует 0xFFFFFFF0. Я ожидаю, что это было сделано для совместимости с 8086 - как он, так и более современные процессоры, кажется, используют 0xFFFF0, но 32-битные процессоры фактически получают доступ 0xFFFFFFF0(отображается в BIOS ROM).
Луаан
26

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

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

psusi
источник
1
Большое спасибо за ответ, но @LawrenceC дал более подробную информацию о его ответе и помог мне понять, как все это работает. В любом случае, благодарю Вас! Я даю вам голос: 3
Фернандо Паладини
13

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

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

Теперь, почему именно 0xFFFFFFF0? Процессор не знает, сколько там BIOS. Некоторые BIOS могут занимать всего несколько килобайт, в то время как другие могут занимать полные мегабайты памяти - и я даже не вхожу в различные дополнительные RAM. Процессор должен быть жестко привязан к какому-либо адресу для запуска - нет необходимости настраивать процессор. Но это только сопоставление адресного пространства - адрес отображается непосредственно в микросхему ПЗУ BIOS (да, это означает, что вы не получите доступ к полному 4 ГБ ОЗУ в этот момент, если у вас есть так много - но в этом нет ничего особенного, многим устройствам требуется свой диапазон адресов). На 32-битном процессоре этот адрес дает вам полные 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 ГиБ) - опять же, чтобы минимизировать конфликты с используемой пользователем памятью, хотя на самом деле это не проблема виртуальной памяти ( приложения не приближаются к реальному аппаратному адресному пространству - у них есть собственный виртуализированный кусок памяти, который может быть сопоставлен, например, с ОЗУ, ПЗУ, устройствами или файлом подкачки).

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

Luaan
источник
3
Отличный ответ, просто чтобы расширить и сказать, что современные процессоры начинают отказываться от хакерских атак, таких как маскирование линий A20 , поэтому поддержка более старых пограничных случаев умирает.
Basic
2
К последнему абзацу: BIOS не может использовать стек «свободно»: он не может записывать в ПЗУ (которое 0xFFFFFFECбудет отображаться). Это означает не только нет, pushно и, например, нет call. Они должны ждать, пока RAM не будет готова.
Ви
7

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

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

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

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

Supercat
источник
Хороший вопрос - вы не найдете ни одного 64-разрядного оборудования, которое будет поддерживать что-либо, близкое к адресуемой 64-разрядной памяти (или даже 1x10 ^ -12 из этого).
Basic
3

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

Agguro
источник
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 имеют случайную чепуху.

viktorkh
источник