Глава 9. Определение количества физической памяти.
Начнём с процедуры определения количества физической памяти, установленной в компьютере. К сожалению, мне не удалось найти документации по BIOS-у, в которой было бы описан доступ к переменным, хранящим размер памяти установленной в компьютере. Вообще говоря, вы можете получить dw-размер расширенной памяти по адресу 0040:0013h или прочитать его из CMOS, но это подразумевается память, размером не более 16Мб. Для i386 это, скорее всего, подходит, но для всех остальных компьютеров - нет.
Вашему вниманию предлагается процедура "get_phys_mem_size", которая сканирует всю физическую память и вычисляет её размер. Этот алгоритм имеет только одно ограничение - размер памяти не должен превышать 4Гб, т.к. мы пока рассматриваем 32-разрядную страничную организацию памяти.
Принцип работы данной процедуры простой - она записывает в память число, а затем считывает его. Если было прочитано записанное число - значит данная ячейка памяти присутствует в компьютере, если было прочитано другое число, то возможны 3 варианта:
1. | Было обращение к ПЗУ (ROM). |
2. | Физическая память по данному адресу не присутствует. |
3. | Физическая память повреждена. |
Третий вариант можно сразу отбросить, потому что наша программа будет работать после запуска компьютера, следовательно, POST уже будет выполнен и память признана пригодной для использования. Если же вы пишите программу для использования в составе BIOS, которая как раз и должна определить наличие и размер памяти, то наш пример будет недостаточен и вам обязательно понадобится получить от производителей техническую документацию по программированию чипсета и процессора, установленных на конкретной материнской плате.
Первый вариант, с обращением к ПЗУ также можно обойти, если начинать исследование памяти со второго мегабайта, т.к. ПЗУ размещено в конце первого мегабайта. При этом будет подразумеваться, что в компьютере установлен минимум - мегабайт памяти; это вполне разумное допущение, т.к. 32-разрядные процессоры обычно снабжают не меньшим количеством памяти. Если же вы пишете ОС, которая должна управлять контроллером или компьютером на базе 32-разрядного процессора с памятью, менее 1Мб, то приводимая в примере процедура вам не понадобится - используйте CMOS.
Итак, наша процедура определения размера физической памяти предназначена только для компьютеров и исправной памятью, размер которой - не менее 1Мб.
Для того, чтобы сканировать память не обязательно проверять каждый байт адресного пространства. Микросхемы памяти по объёму кратны, как правило, целому числу мегабайт, но даже если будут использоваться микросхемы меньшего размера, можно гарантировать, что они будут иметь объём, кратный 4Кб. Это значение взято не спроста - такой же размер имеет, как вы помните, физическая страница памяти и минимальная логическая страница (а мы пока только такие и рассматриваем), поэтому достаточно записать и затем прочитать одну ячейку памяти в каждой физической странице и проверить это значение.
При проверке считанного значения применяется "неразрушающий" контроль, который обеспечивается двумя командами XCHG:
xchg es:[ edi ],eax ; Поменяли значениями регистр ; и ячейку памяти. xchg es:[ edi ],eax ; Вернули прежнее значение обратно ; в память. Теперь в регистре число, ; которое "побывало" в ячейке памяти. ; Если оно не изменилось - значит, ; запись была в ОЗУ.
Как правило, при считывании числа из неприсутствующей физической памяти, мы всегда получаем значение, одинаковое для каждого байта, например, FFh:
mov edx,12345678h mov eax,edx xchg es:[ edi ],eax ; EAX = FFFF FFFFh xchg es:[ edi ],eax ; EAX = FFFF FFFFh ; EAX <> EDX, значит, что ES:EDI указывают на не-ОЗУ.
При обращении к ОЗУ мы получим то, что туда записали:
mov edx,12345678h mov eax,edx xchg es:[ edi ],eax ; EAX = FFFF FFFFh xchg es:[ edi ],eax ; EAX = 1234 5678h ; EAX = EDX
Далее приводится процедура "get_phys_mem_size":
get_phys_mem_size proc near ; Вычисляет размер физической памяти, установленной в компьютере ; Возврат: EAX = размер памяти в байтах push bx push ecx push edx push ebp push es push edi mov ax,All_mem_sel ; Для проверки памяти мы будем mov es,ax ; использовать сегмент, описывающий ; всю память. mov bx,Page_Directory_adr ; DS:BX указывает на ; начало каталога страниц. add bx,1000h + 256 * 4 ; А теперь - на первую страницу ; второго мегабайта памяти в первой ; таблице страниц. mov edx,12345678h ; Это тестовое значение, которое ; мы будем записывать и затем ; считывать из памяти. mov ebp,1024 * 1024 * 2 ; В EBP будет находится адрес, ; с которого мы будем проверять ; память. gpms_1: ; Подготавливаем в первой определённой таблице страниц 256 элементов PTE, ; начиная с 256. Они будут описывать второй мегабайт адресного пространства. ; Эти PTE мы отобразим не один мегабайт физической памяти, начиная с адреса ; в EBP. mov edi,1024 * 1024 mov eax,ebp mov cx,256 mov al,3 ; Записывая в AL 3, мы тем самым устанавливаем ; биты 0 и 1 этого регистра. Когда мы запишем EAX ; на место какого-либо элемента PTE в таблице ; страниц, то эти биты определят этот элемент как ; присутствующий (бит P=1) с разрешённым доступом ; по чтению записи (бит R/W=1). ; Записываем 256 элементов PTE: gpms_2: mov [ bx ],eax add eax,1000h add bx,4 loop gpms_2 mov eax,cr3 ; Перегружаем значение CR3. Это вызовет mov cr3,eax ; сброс буферов TLB и заставит процессор ; использовать обновлённые значения из ; изменённой нами таблицы страниц. jmp $+2 ; Выполняя команду перехода, мы сбрасываем ; конвейер команд процессора, заставляя его заново ; считывать текущую часть кода и очистить результаты ; всех спекулятивно выполненных команд. Для нас это ; гарантия того, что процессор будет работать с ; обновлённой таблицей страниц. sub bx,256 * 4 ; Возвращаем указатель на 256-й элемент ; PTE в таблице страниц. mov cx,256 ; Тестируем память: gpms_3: mov eax,edx ; EAX = 12345678h. xchg eax,es:[ edi ] ; Пара команд XCHG выполнит "не xchg eax,es:[ edi ] ; разрушающий" контроль памяти. add edi,1000h cmp eax,edx ; Если было прочитано то же значение, что ; и записано, значит текущая страница памяти ; отображена на ОЗУ. jne gpms_4 ; Иначе - мы дошли до конца ОЗУ. loop gpms_3 add ebp,1000h * 256 ; Здесь меняется адрес физической ; памяти, на который будет отображён ; следующий мегабайт. jmp gpms_1 gpms_4: mov eax,ebp add eax,edi sub eax,1000h * 257 ; EAX содержит адрес последней ; реально присутствующей физической ; страницы памяти, это и есть размер ; физической памяти. ; Очистка элементов PTE, отображённых на второй мегабайт (т.е. возврат ; таблицы страниц в первоначальное состояние. mov edx,eax mov ax,ds mov es,ax mov di,Page_Directory_adr add di,256 * 4 mov cx,256 xor eax,eax cld rep stosd mov eax,edx ; EAX = размер физической памяти в байтах. pop edi pop es pop ebp pop edx pop ecx pop bx ret endp
Следующая глава | Оглавление | Вопросы? Замечания? Пишите: sasm@narod.ru |
Copyright © Александр Семенко. |