Що таке Protected Mode та з чим його їдять. Перемикання процесорів Intel на захищений режим Перехід на захищений режим асемблер

РПУМЕ ОЕДЕМШОПЗП РЕТЕТЩЧБ ЧОПЧШ НПЗХ ПВТБДПЧБФШ ЧБУ ОПЧЩН ЧЩРХУЛПН УЧПЕК ТБУУЩМЛЙ.

ЙФБЛ, ЛБЛ ЧЩ РПНОЙФЕ, Ч ​​ТЕБМШОПН ТЕЦЙНЕ ТБВПФЩ РТПГЕУУПТБ ОБН ДПУФХРЕО ЧУЕЗП МЙИШ 1 НЕЗБВБКФ БДТЕУОПЗП РТПУФТБУФЧБ МІШ 640 ЛЙМПВБКФ). ФБЛ ЬФП Й ВЩМП ПП ЧТЕНЕОБ РЕТЧЩІ РТПГЕУУПТПЧ ЧТПДЕ 8086, ВП РПУФЕРЕООП ПВ'ЯНБ ПРЕТБФЙЧОПК РБНСФЙ УФБМП ОЕ ІЧБФБФШ. ч ФП ЦЕ ЧТЕНС ФТЕВПЧБМПУШ УПІТБОЙФШ РПМОХА ПВТБФОХА УПЧНЕУФЙНПУФШ, ЮФПВЩ 16-ТБЪТСДОЩЕ ПРЕТБГЙПООЩЕ УЙУФЕНЩ ЧТПДЕ DOS УНПЗМШФТПТ. РПЬФПНХ ВЩМ ЧЧЕДО ОПЧЩК ТЕЦЙН ТБВПФЩ РТПГЕУУПТБ - 'БЕЙЕЈООЩК ТЕЦЙН. РПУМЕ РЕТЕІПДБ Ч ОЕЗП ДМС БДТЕУБГЙЙ ЙУРПМШЪХЕФУС ОЕ 16, Б 32 ЙМЙ ДБЦЕ 64 ВЙФБ, Б УЕЗНЕОФЩ Ч УФБТПН РПОЙНБОЙЙ ЙУЮЄЪБАФ. фБЛЦЕ ДПВБЧМСАФУС 'БЕЙФОЩЕ НЕИБОЙЪНЩ (ЙНЕООП РПЬФПНХ) ЪБЕЙЕЈООЩКТЕЦЙН), ЮФПВЩ СДТП пу ВЩМП ЙЪПМЙТПЧБООП ПФ РТЙМПЦЕОЙК Й НПЗМП ЙНЙ УЧПВПДОП ХРТБЧМСФШ. ЬФП ОЕПВІПДЙНП МАВПК РПМОПГЕООПК НОПЗП'БДБЮОПК УЙУФЕНЕ.

оБЮОЈН У ФПЗП, ЮФП ДПВБЧМСЕФУС Ч ТЕБМШОПН ТЕЦЙНЕ ПРО РТПГЕУУПТБІ, ЛПФПЩЕ РПДДЕТЦЙЧБАФ 32-ВЙФОЩК БДТЕУ (i386 Й ЧЩЕ) БУЙТСАФУС УФБТЩЕ: EAX, EBX, ECX, EDX, ESP, EBP, EIP, ESI, EDI, EFLAGS. лБЛ НВЦОП ДПЗБДБФШУС, ЬФП 32-ВЙФОЩЕ ЧЕТУЙЙ ПВЩЮОЩІ ТЕЗЙУФТПЧ ТЕБМШОПЗП ТЕЦЙНБ (Л ЙНЕОЙ ТЕЗЙУФТБ ДПВБЧМСЕФУС РТЙУФБЧ. чУЕ ЬФЙ 32-ВЙФОЩЕ ТЕЗЙУФТЩ ЛТПНЕ EIP ДПУФХРОЩ Й Ч ТЕБМШОПН ТЕЦЙНЕ, ОП Ч ФБЛПН УМХЮБЕ ВХДХФ ЪБОЙНБФШ ОБ 1 ВБКФ ВПМШБВР У). про РТПГЕУУПТЕ НПМПЦЕ 286 ЬФЙ ЛПНБОДЩ ВХДХФ ОЕЛПТТЕЛФОЩ. нЩ НПЦЕН, ОБРТЙНЕТ, ОБРЙУБФШ mov eax, 0x12345678 Й РПУМЕ ЬФПЗП Ч AX ВХДЕФ 0x5678, РПФПНХ ЮФП ПО ЛБЛ ВЩ СЧМСЕФУС "ПЛОПН" ЙЮОП, AL НМБДИБС ЮБУФШ AX). ТЕЗЙУФТБ-ПФПВТБЦЕОЙС УФБТІЕК ЮБУФЙ 32-ВЙФОЩІ ТЕЗЙУФТПЧ ОЕ УХЕЕУФЧХЕФ - НПЦОП ЙЬЧМЕЮШ ФПМШЛП У РПНПЕШУ БТЙЖНЕФЙЛЙ ЧРТБЧП У РПНПЕША shr eax, 16, ФПЗДБ Ч AX ВХДЕФ УФБТИБС РПМПЧЙОБ, ОП УПДЕТЦЙНПЕ НМБДІЇ ВЙФ ВХДЕФ ХФЕТСОП). ЮФП ІБТБЛФЕТОП, Ч 'БЕЙЕЈООПН ТЕЦЙНЕ ОБПВПТПФ, ЛПНБОДЩ ТБВПФЩ У 16-ВЙФОЧНИЙ ТЕЗЙУФТБНЙ (ОП ОЕ 8-ВЙФОЧНИЙ) ФТЕВХАФ РТЕЖ БЪТСДОПУФШ Ч ДЧБ ТБЪБ ВПМШІ, Ч 'БЕЙЕЈООПН ТЕЦЙНЕ ЩУФТІ ЧЩРПМОСАФУС Й ЪБОЙНБАФ НЄШІ НЕУФБ ЙНЕООП ЛПНБОДЩ 32- ВЙФОПК БТЙЖНЕФЙЛ.

ФБЛЦЕ, ФЕРЕТШ Х ОБУ ПРО 2 УЕЗНЕОФОЩІ ТЕЗЙУФТБ ВПМШІ - GS Й FS. ТБВПФБ У ОЙНЙ РПМОПУФША БОБМПЗЙЮОБ DS Й ES Й ЧЩ НПЦЕФЕ ЙІ УЧПВПДОП ЙУРПМШЪПЧБФШ Ч ТЕБМШОПН ТЕЦЙНЕ. ПФМЙЮЙЕ ФПМШЛП Ч ФПН, ЮФП ОЙЛБЛЙЄ ЛПНБОДЩ ЙІ СЧОП ОЕ РПДТБЪХНЕЧБАФ БГЙСНЙ, ES ОЕЛПФПТЩНИЙ УФТПЛПЧЩНИЙ ПРЕТБГЙСНИЙ) Й ОБДП СЧОП ХЛБЪЩЧБФШ, ЮФП ЧЩ ІПФЙФЕ ПВТБЕБФШУС ЮЕТЕЙ ОЙІ. ОБРТЙНЕ, mov ax, .

РПНЙНП ЬФПЗП ТБУЙЙТЕОЙС ТЕЗЙУФТПЧ, ДПВБЧМСАФУС ОПЧШЕ ХРТБЧМСАЕЙЕ ТЕЗЙУФТЩ (ТБОШІ ЧМЙСМ ПРО ТЕЦЙН ТБВПФЩ РТПГЕУУПТБ ФПМ, ФПМШ, 3РПГЕУУПТБ ФПМ, ФПМШ, ФПМШ, ФПМШ, ФПМШ, ФПМШ, ФПМ, ФПМШ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМШ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ФПМ, ПФР, ПФР, ПТП, ФПМ. еУФШ Й ДТХЗЙЕ (ОБРТЙНЕТ, ПФМБДПЮОЩЕ ТЕЗЙУФТЩ), ОП ПІЙ ОБУ УЕКЮБУ ОЕ ЙОФЕТЕУХАФ. йНЕООП У РПНПЕШУ ЬФЙІ ТЕЗЙУФТПЧ РТПЙЪЧПДЙФУС РЕТЕЛМАЮЕОЕ РТПГЕУУПТБ Ч 'БЕЙЕЈООЧК ТЕЦЙН Й ОБУФТПКЛБ ОПЧЩІ ЖХОЛГЙКЙ ЧТПД. пій ДПУФХРОЩ Ч ТЕБМШОПН ТЕЦЙНЕ.

ч 'БЕЙЕЈООПН ТЕЦЙНЕ РПОСФЙЕ УЕЗНЕОФБ ЙЪНЕОСЕФУС. ФЕРЕТШ ЬФП ОЕ РТПУФП ВБЪПЧЩК БДТЕУ, Б ОПНЕТ ЬМЕНЕОФБ (ДЕУЛТЙРФПТБ УЕЗНЕОФБ) Ч УРЕГЙБМШОПК ФБВМЙГЕ. ФБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ УПЪДБЈФУС ПРЕТБГЙПООПК УЙУФЕНПК Й НПЦЕФ УПДЕТЦБФШ ОЕПВІПДЙНПЕ ЛПМЙЮЕУФЧП ПРЙУБОЙК УЕЗНЕ. лБЦДЩК ЬМЕНЕОФ ФБВМЙГЩ ЪБОЙНБЕФ 8 ВБКФ Й Ч УРЕГЙБМШОПН ЖПТНБФЕ ПРЙУЩЧБЕФ ВБЪПЧЩК БДТЕУ УЕЗНЕОФБ, ТБЪНЕТ, РТБЧБ ДПУФХРБ.

УЕЗНЕОФЩ 'БЕЙЕЈООПЗП ТЕЦЙНБ ДЕМСФУС ПРО ДЧБ ФЙРБ - УЕЗНЕОФЩ ЛПДБ Й УЕЗНЕОФЩ ДБООЩІ (ПРО УБНПН ДЕМА ЄУФШ ЕЕБ ЧУСТЛЙЕ TSS Й LDT, ОП ч CS НВЦОП ЪБЗТХЦБФШ ФПМШЛП ОПНЕТБ ДЕУЛТЙРФПТПЧ, ПРЙУБООЩІ ЛБЛ УЕЗНЕОФ ЛПДБ, Ч ПУФБМШОЩ УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ НПЦОПФБЗБ , ФБЛ Й ЛПДБ. чБЦОБС ТБЪОЙГБ Ч ФПН, ЮФП УЕЗНЕОФ ЛПДБ НВЦОП ФПМШЛП ЮЙФБФШ Й ЙУРПМОСФШ, Б УЕЗНЕОФ ДБООЩІ ФПМШЛП ЮЙФБФШ Й РЙУБФШ. л УЮБУФША, УЕЗНЕОФЩ НПЗХФ РЕТЕЛТЩЧБФШУС Ч РБНСФЙ, РПЬФПНХ НПЦОП УПЪДБФШ ДЧБ ДЕУЛТЙРФПТБ, УУЧМБАЕЙЄУС ПРО ПДЙО Й ФПФ ЦЕ ТЕЗЙ ЙУРПМОСЕНЩН, Б ДТХЗПК ДПУФХРОЩН ДМС ЪБРЙУЙ.

оЕУНПФТС ПРО РПДДЕТЦЛХ УЕЗНЕОФБГЙЙ, ПІБ УЮЙФБЕФУС ХУФБТЕЧОК. ОС Windows, ОЙ Linux ОЕ ЙУРПМШЪХАФ ЕЈ Ч РПМОПК НЕТЕ, Б ПРО ПФМЙЮОЩІ ПФ x86 БТІЙФЕЛФХТЩ (ОБРТЙНЕТ, ARM) ПОБ ППЧУЄ ПФУХФУФЧХЕФ. дМС ТБЪЗТБОЙЮЕОЙС ДПУФХРБ Л РБНСФЙ ЙУРПМШЪХЕФУС ЗПТБЪДП ВПМЕЕ ЗЙВЛЙК НЕИБОЙЪН УФТБОЙЮОПК БДТЕУБГЙЙ, ЛПФПТЩК НЩ ТБУУНПФТЙ. ЮФПВЩ ЙЪВБЧЙФШУС ПФ УЕЗНЕОФБГЙЙ пу РТПУФП ПРЙУЩЧБЕФ ФБВМЙГХ ЙЪ ДЧХІ ДЕУЛТЙРФПТПЧ, Х ЛБЦДПЗП ЙЪ ЛПФПТЩІ ВБЪПЧЩБД ТБЪНЕТ БДТЕУХЕНПК РБНСФЙ Ч 32-ВЙФОПН ТЕЦЙНЕ). ч ФБЛПН УМХЮБЕ ЗПЧПТСФ, ЮФП НЩ ЧЛМАЮЙМЙ ТЕЦЙН МЙОЕКОЩІ БДТЕУПЧ - УНЕЕЕОЙЕ УППФЧЕФУФЧХЕФ ЖЙЬЮЮУЛПНХ БДТЕУХ. ЬФП ПЮЕОШ ХДПВОП Й З РПКДХ РП ФПНХ ЦЕ РХФЙ. оЕ УМЕДХЕФ РЩФБФШУС ЙУРПМШЪПЧБФШ УЕЗНЕОФБГЙА Ч УЧПЕК ПРЕТБГЙПООПК УЙУФЕНЕ - ЬФП УЙМШОП ХУМПЦОСЕФ ЛПД СДТБ, СЬЩЛЙ ЧЩУПЛПЗДХТ ЦЙЧБАФ УЕЗНЕОФБГЙА (ФП ЄУФШ ЧЩ УНПЦЕФЕ РПМОПГЕООП РТПЗТБННЙТПЧБФШ ФПМШЛП ПРО Assembler) Й, ОБЛПОЄГ, ЧЩ ОЕ УНПЦЕФЕ РЕТЕОЕУФ УЙУФЕНХ ПРО ДТХЗХА БТІЙФЕЛФХТХ, РПФПНГ ЮФП x86 ЕДЙОУФЧЕООБС, ЛПФПТБС ХНЕЄФ ЬФПФ НЕИБОЙЪН (Й ФП, Ч 64-ВЙФОПН ТЕЦЙНЕ РПМЗББ ЙЗОПТЙТХАФУС, Б ЙУРПМШЪХЕФУС МЙИШ ЙОЖПТНБГЙС П РТБЧБІ ДПУФХРБ).

лБЛ З ХЦЕ УЛБЪБМ, ФБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ЖПТНЙТХЕФУС УБНПК ПРЕТБГЙПООПК УЙУФЕНПК. юФПВЩ ХЛБЪБФШ РТПГЕУУПТХ, ТУД ПОБ ОБІПДЙФУС ЙУРПМШЪХЕФУС УРЕГЙБМШОБС ЛПНБОДБ - lgdt (Load Global Descriptor Table). поб РТЙОЙНБЕФ 6-ВБКФПЧХА РЕТЕНЕООХА Ч РБНСФЙ. РЕТЧШЕ ЕЈ 16 ВЙФ УПДЕТЦБФ ТБЪНЕТ ФБВМЙГЩ Ч ВБКФБИ (ФБЛЙН ПВТБЪПН, НБЛУЙНБМШОПЕ ЛПМЙЮЕУФЧП ДЕУЛТЙРФПТПЧ - 65536 / 2 = В2 = 2) ЪПЧЩК МЙОЕКОЩК БДТЕУ Ч РБНСФЙ УБНПК ФБВМЙГЩ (ФП ЄУФШ ВЕЪ ХЮЈФБ ЧУЕЇ УЕЗНЕОФПЧ). йНЕЕФ УНЩУМ ЧЩТБЧОСФШ ОБЮБМП ФБВМЙГЩ ПРО 16 ВБКФ, РПФПНГ ЮФП ЬФП ХМХЮИБЕФ УЛПТПУФШ ДПУФХРБ Л ЕЬЬЬЬНЕНЕОФБН. рЕТЧЩК ЬМЕНЕОФ ФБВМЙГЩ ЧУЄЗДБ ДПМЦЕО ВЩФШ ТБЧЕО ОХМА Й МАВПЕ ЙУРПМШЪПЧБОЙЕ ОХМЕЧПЗП УЕМЕЛФПТБ(ХЛБЪБФЕМШ ПРО ЬМЕНЕОФ ФБВМЙГЩ ДЕУЛТЙРФПТПЧ Ч УЕЗНЕОФОПН ТЕЗЙУФТЕ ОБЩЩБЕФУС ФБЛ) РТЙЧПДЙФ Л ПІЙЛЕ. 'ОБЮЙФ ВПМЕЕ-НЕОЕЕ ТБВПФПУРПУПВОБС ФБВМЙГБ ДЕУЛТЙРФПТПЧ ДПМЦОБ УПДЕТЦБФШ ІПФС ВЩ ФТЙ ДЕУЛТЙРФПТБ - РХУФПК, ДЕУЛТЙРФПТ ЛП.

оХ ЮФП ЕЕЈ УФПЙФ ТБУУЛБЪБФШ, РТЕЦДЕ, ЮЕН НЩ РПРТПВХЕН РЕТЕКФЙ Ч 'БЕЙЕЈОЩК ТЕЦЙН? рПЦБМХК, ЕЕЈ УФПЙФ ХРПНСОХФШ РТП ХТПЧИЙ ДПУФХРБ. лПД СДТБ УЙУФЕНЩ Й ЛПД РТЙМПЦЕОЙК ПФДЕМЕОЩ ДТХЗ ПФ ДТХЗБ У ФПК ГЕМША, ЮФПВЩ СДТП НПЗМП РПМОПУФША ХРТБЧМСФШ РТПГЕУУПТПН, Б РТПГЕУУПТПН ПФХ СДТБ (ЧЕДШ Х ОБУ НОПЗП'БДБЮОБС пу). лПД ЙУРПМОСЕФУС У ПРТЕДЕМЈООЧН ХТПЧОЕН РТЙЧЙМЕЗЙК. ч x86 ЙІ ГЕМЩІ 4 ІФХЛЙ - ПФ 0 ДП 3. ОХМЕЧПК ХТПЧЕОШ УБНЩК РТЙЧЙМЕЗЙТПЧБООЩК НЩК "ВЕУРТБЧОЩК". лБЛ Й Ч УМХЮБЕ У УЕЗНЕОФБГЙЄК, ТБЪТБВПФЮЙЛЙ x86 РЕТЕВПТЕЙМЙ У ЖХОЛГЙПОБМПН Й ЧУЕ пу ХТЩ РТПГЕУУПТБ РПДДЕТЦЙЧБАФ ФПМШЛП ЙІ. х ЛБЦДПЗП УЕЗНЕОФБ Ч ЕЗП ДЕУЛТЙРФПТЕ ХЛБЪБО DPL (Descriptor privilege level)- ХТПЧЕОШ ДПУФХРБ ОЕПВІПДЙНЩК ДМС ДБООПЗП УЄЗНЕОФБ. ОЕРТЙЧЙМЕЗЙТПЧБООЩК ЛПД ОЕ НПЦЕФ РПМХЮЙФШ ДПУФХР Л УЕЗНЕОФХ У ХТПЧОЕН ДПУФХРБ 0, Б РТЙЧЙМЕЗЙТПЧБООЩК ЛПД НПЦЕФ РПМХЮЙФЗ ДПУ.

уЕМЕЛФПТ УЄЗНЕОФБ, ЛПФПТЩК УПДЕТЦЙФУС Ч УЄЗНЕОФОПН ТЕЗЙУФТЕ, СЧМСЕФУС ОЕ РТПУФП ОПНЕТПН ЬМЕНЕОФБ Ч ФБВМЙГЕ, ОП Й ХЛБЪБФЕМЕНХТ ЦБФ ХТПЧЕОШ РТЙЧЙМЕЗЙК (ПФ 0 ДП 3), Б ХЦЕ УФБТИЙЕ ОПНЕТ УБНПК ФБВМЙГЩ. фБЛЙН ПВТБЪПН УЕМЕЛФПТ = (ЙОДЕЛУ_ДЕУЛТЙРФПТБ shl 2) + RPL. RPL- Requested privelege level - ЪБРТБИЙЧБЕНЩК ХТПЧЕОШ РТЙЧЙМЕЗЙК. РТЙ ЬФПН RPL ДПМЦЕО ВЩФШ ВПМШІ ЙМЙ ТБЧЕО НБЛУЙНБМШОПНХ ЙЪ DPL Й CPL (Current privilege level). CPL ТБЧЕО RPL УЕМЕЛФПТБ Ч CS. ФБЛЙН ПВТБЪПН ЛПД ОЕ НПЦЕФ РПМХЮЙФШ ДПУФХРБ Л УЕЗНЕОФБН, Х ЛПФПТЩІ ХТПЧЕОШ ДПУФХРБ Ч ЮЙУМПЧПН ЧЙДЕ ОЙЦЕ, ЮЕН Х ОЕЗП УБНПЗП. с, ЧЕТПСФОП, ПРЙУБМ ДПУФБФПЮОП ЪБРХФБООП, ВП ЧРПМИЙ НВЦОП ПВПКФЙУШ RPL = DPL, ЛБЛ НЩ Й РПУФХРЙН.

рПЛБ НЩ РЙЫЕН ФПМШЛП СДТП, НЩ ВХДЕН ТБВПФБФШ Ч ОХМЕЧПН ЛПМШГЕ ЪБЭЙФЩ (ФБЛ ЕЭЈ ОБЪЩЧБАФ ХТПЧОЙ РТЙЧЙМЕЗЙК), ЮФПВЩ ЙНЕФШ РПМОЩК ДПУФХР Л БРРБТБФХТЕ.

уЕЗНЕОФБГЙС ОБН ОЕ ОХЦОБ, РПЬФПНХ З ОЕ ВХДХ ПУФБОБЧМЙЧБФШУС РПЛБ ЮФП ПРО ЖПТНБФЕ ДЕУЛТЙРФПТБ, Б ДБН ЗПФПЧЩЕ 'ОБЮЕОЙС. еУМЙ ​​ЙОФЕТЕУОП, НВЦЕФЕ РПЮЙФБФШ ЬФХ УФБФША . ТБУУНПФТЙН РТПУФЕКІЙК ЛПД РЕТЕІПДБ Ч 'БЕЙЕЈООЩК ТЕЦЙН.

; 'БРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П Ь БРХУЛІ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; 'БЗТХЬЙН ЪОБЮЕОЙЕ Ч GDTR lgdt; 'БРТЕФЙН РТЕТЩЧБОЙС cli ; РЕТЕКДЕН Ч 'БЕЙЕЈООЩК ТЕЦЙН mov eax, cr0 or eax, 1 mov cr0, eax ; РЕТЕКДН ПРО 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF; CODE-8 dq 0x00CF92000000FFFF; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32; 32-ВЙФОЩК ЛПД use32 start32: ; ОБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax movzx esp, sp; чЩЧПДЙН УЙНЧПМ ПРО ЬЛТБО mov byte, "!" ; 'БЛЮТНІЙОЕ jmp $

ЬФПФ ЛПД УМЕДХЕФ ДПРЙУБФШ Л ЗОБІЙНИХ ОБЮБМШОПНХ ЪБЗТХЪЮЙЛХ.

РЕТЕД РЕТЕІПДПН Ч 'БЕЙЕЈООЩК ТЕЦЙН ОЕПВІПДЙНП ЪБРТЕФЙФШ РТЙЈН БРРБТБФОЩІ РТЕТЩЧБОЙК (ЛМБЧЙБФХТБ, НЩИШ, ФБКНЕТ ЙДФ РПУМЕ РЕТЕІПДБ ПУФБФУС ОЕ Х ДЕМ, Б УЧПЙ ПВТБВПФЮЙЛЙ НЩ ЕЕЈ ОЕ ОБРЙУБМЙ, РПЬФПНХ РЕТЧПЕ ЦЕ РТЕТЩЧБОЙЕ ПВТХІЙФ УЙУФЕНХ.

оЕРПУТЕДУФЧЕООП РЕТЕІПД Ч 'БЕЙЕЈООЩК ТЕЦЙН ПУХЕЕУФЧМСЕФ ХУФБОПЧЛБ ОХМЕЧПЗП ВЙФБ Ч CR0. йНЕООП ЬФП НЩ Й ДЕМБЕН (РТСНПК ДПУФХР Л CR0,2,3,4 ОЕЧПЪНПЦЕО ФБЛ ЦІ ЛБЛ Й Л УЕЗНЕОФОЧНИЙ ТЕЗЙУФТБН, РПЬФПНХ ЙУРПМШЪХЕН EAX). оЕУНПФТС ПРО ФП, ЮФП НЩ ХЦЕ РЕТЕИМЙ Ч 'БЕЙЕЈООЩК ТЕЦЙН, ЛПД РТПДПМЦБЕФ ЙУРПМОСФШУС РП-РТЕЦОЕНХ 16-ВЙФОЩК. дМС ПЛПОЮБФЕМШОПЗП РЕТЕІПДБ ОБН ОХЦОП ПВОПЧЙФШ УПДЕТЦЙНПЕ УЕЗНЕОФОЩІ ТЕЗЙУФТПЧ. дЙТЕЛФЙЧБ БУУЕНВМЕТБ use32 ЗПЧПТЙФ ЕНХ, ЮФП ДБМШОЕКИЙК ЛПД ЧЩРПМОСЕФУС Ч ЬБЕЙЕЈООПН ТЕЦЙНЕ Й ОЕПВІПДЙНП РЕТЕЛМАЮЙФШУС Ч ТЕЦЙ 6-ВЙФОПЗП (ПО ЙУРПМШЪХЕФУС РП ХНПМЮБОЯ) .

лПНБОДБ movzx ТБУЙТСЕФ ЧФПТПК БТЗХНЕОФ ДП РЕТЧПЗП. год УНЩУМЕ, ЮФП ЙЪ 16 ВЙФОПЗП ЪОБЮЕОЙС SP РПМХЮБЕФУС 32-ВЙФОПЕ. УФБТИЙ ВЙФЩ ПВОХМСАФУС (НБМП МЙ, ЮФП ФБН ВЩМП ДП ОБУ). рТЕДРПУМЕДОСС ЛПНБОДБ ДЕНПОУФТЙТХЕФ ОБН ПП'НПЦОПУФЙ 'БЕЙЕЈООПЗП ТЕЦЙНБ - НЩ ПВТБЕБЕНУС РП БВУПМАФОПНХ 32-ВЙФОПНХ БДТЕУХ Л ЧЙДЧП-ЧП З УЙНЧПМ "!" ЧРТБЧЩК ОЙЦОЙК ХЗПМ ЬЛТБОБ ЧЕФБ).

нЩ ВПМШЕ ОЕ НПЦЕН ПВТБЕБФШУС Л УЄТЧЙУБН BIOS, ФЕРЕТШ РТЙИМП ЧТЕНС ОБН УФБФШ РПМОПУФША УБНПУФПСФЕМШОЩНИЙ Й УБНЙН ХРТБЧМСФШЧУН. РЕТЕЪБЗТХЦБФШУС Й ЦДБФШ ОБЦБФЙС ПРО ЛМБЧИХ НЩ РПЛБ ОЕ ХНІЄН, РПЬФПНХ РТПУФП ЪБЧЙУБЕН У РПНПЕШУ ЛПНБОДЩ jmp $ (РЕТЕІПД ПРО ФХ ЦХ ).

ч ЗБІЙЛЕННЯ boot.cfg ЛПНБОДХ S64 РПЛБ ЪБНЕОЙН ПРО S32. ФЕРЕТШ, ЕУМЙ ЧЩ ЧУЈ РТБЧЙМШОП УДЕМБМЙ, ОБИ ЪБЗТХЪЮЙЛ ВХДЕФ ЪБЧЕТЫБФШ УЧПА ТБВПФХ ЧЩЧПДПН ЧПУЛМЙГБФЕМШОПЗП ОБЛБ Ч ХЗПТ Б. ьФП ФПМШЛП ОБЮБМП. нЩ ОБЛПОЄГ-ФП РТБЛФЙЮЕУЛЙ ХИМЙ ЙЪ ТЕБМШОПЗП ТЕЦЙНБ (ПРО УБНПН ДЕМА ФБН ЕЕЈ ПУФБМПУШ ОЕНОПЗП ДЕМ) Ч 'БЕЙЕЈОЩК. РПУЛПМШЛХ ОБИ 'БЗТХ'ЮЙЛ ЧЩРПМОСЕФУС Ч ОХМЕЧПН УЕЗНЕОФЕ ТЕБМШОПЗП ТЕЦЙНБ, ЧУЕ УНЕЕЕОЙС УППФЧЕФУФЧХАФ ЖЙЬЙЮЕУЛЙН БДТЕУБЙЙ Н, ОБН ОЕ РТЙИМПУШ ОЙЮЕЗП РЕТЕУЮЙФЩЧБФШ.

ч ЪБЧЕТЫЕОЕ ЧЩРХУЛБ, РПЦБМХК, ДПВБЧМА РПУМЕДОЙК ЫФТЙИ - РТПЧЕТЛХ, ЮФП РТПГЕУУПТ РПДДЕТЦЙЧБЕФ ЪБЕЙЕЈООЩК ТЕЦЙН. уХФШ РТПЧЕТЛЙ Ч ФПН, ЮФП ОЕ ЧУЄ ВЙФЩ FLAGS НВЦОП ЙЪНЕОЙФШ РТПЗТБННОП. ФП ЄУФШ ТЕЗЙУФТ ОЕ УПЧУЕН 16-ВЙФОЩК. ОБ ОПЧЩІ РТПГЕУУПТБІ ДПУФХРОП ДМС ЙЪНЕОЕОЙС ВПМШІ ВЙФ Й ЬФП НВЦОП ПВОБТХЦЙФШ. ТБЪВЕТЙФЕ ЛПД ОЙЦЕ УБНЙ, УЛБЦХ ФПМШЛП, ЮФП ЛПНБОДБ pushf РПНЕЕБЕФ ТЕЗЙУФТ ЖМБЗПЧ Ч УФЕЛ, Б popf ЧЩФБМЛЙЧБЕФ УПДЕТЦЙНПЕ УФЕЛБ ПП F ФБЛЙН ПВТБЪПН ЕЗП НПЦОП НЕОСФШ ГЕМЙЛПН, Б ОЕ ПФДЕМШОЧНИЙ ЛПНБОДБНИЙ. чПФ РПМОЩК ЛПД ОБИЗП ЪБЗТХЪЮЙЛБ:

Org 0x7C00 jmp boot; 'БЗПМПЧПЛ ListFS align 4 fs_magic dd? fs_version dd? fs_flags dd? fs_base dq? fs_size dq? fs_map_base dq? fs_map_size dq? fs_first_file dq? fs_uid dq? fs_block_size dd? ; 'БЗПМПЧПЛ ЖБКМБ virtual at 0x800 f_info: f_name rb 256 f_next dq? f_prev dq? f_parent dq? f_flags dq? f_data dq? f_size dq? f_ctime dq? f_mtime dq? f_atime dq? end virtual; дБООЩЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ label sector_per_track слово в $$ label head_count byte в $$ + 2 label disk_id byte в $$ + 3 reboot_msg db "Press any key...",13,10,0 boot ; чЩЧПД УФТПЛЙ DS:SI ПРО ЬЛТБО write_str: push si mov ah, 0x0E @: lodsb test al, jz @f int 0x10 jmp @b @: pop si ret ; лТЙФЙЮЕУЛБС ПИЙВЛБ error: pop si call write_str ; РЕТЕЪБЗТХЪЛБ reboot: mov si, call reboot_msg write_str xor ah, ah int 0x16 jmp 0xFFFF:0 ; 'БЗТХЪЛБ УЕЛФПТБ DX:AX Ч ВХЖЕТ ES:DI load_sector: push dx add ax, word adc dx, word cmp byte, 0xFF je .use_EDD dl, mov bx, di mov al, 1 mov si, 3 @: mov ah, 2 int 0x13 jnc @ f xor ah, ah int 0x13 dec si jnz @ b .error: call error db "DISK ERROR",13,10 ,0 @: pop si cx bx dx ret .use_EDD: push si mov byte, 0x10 mov byte, 0 mov word, 1 mov , di , 0x42 mov dl, mov si, 0x600 int 0x13 jc. error pop si dx ret; РПЙУЛ ЖБКМБ У ЙНЕОЕН DS:SI Ч ЛБФБМПЗЕ DX:AX find_file: push cx dx di . 10,0 @: mov di, f_info Call load_sector push di mov cx, 0xFFFF xor al, al repne scasb neg cx dec cx pop di push si repe cmpsb pop si je . found: pop di dx cx ret; 'БЗТХЪЛБ ФЕЛХЕЕЗП ЖБКМБ Ч РБНСФШ РП БДТЕУХ BX:0. лПМЙЮЕУФЧП ЪБЗТХЦЕООЩИ УЕЛФПТПЧ ППЪЧТБЕБЕФУС Ч AX load_file_data: push bx cx dx si mov ax, word mov dx, word .load_list: ax, bx pop bx sub ax, bx shr ax, 9 - 4 ret @: mov di, 0x8000 / 16 call load_sector mov si, di mov cx, 512 / 8 - 1 .load_sector: lodsw mov dx, add si, 6 cmp ax, -1 in @f cmp dx, -1 je .file_end @: push es mov es, bx xor di, di call load_sector add bx, 0x200 / 16 pop es loop .load_sector lodsw mov dx, jmp .load_list ; фПЮЛБ ЧИПДБ Ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ boot: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ jmp 0:@f @: mov ax, cs mov ds, ax mov es, ax; ОБУФТПЙН УФЕЛ mov ss, ax mov sp, $$; ТБ'ТЕЙЙН РТЕТЩЧБОЙС sti ; 'БРПНОЙН ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov, dl; ПРТЕДЕМЙН РБТБНЕФТЩ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov ah, 0x41 mov bx, 0x55AA int 0x13 jc @f mov byte, 0xFF jmp .disk_detected @: error inc dh mov , dh and cx, 111111b mov, cx .disk_detected: ; 'БЗТХЬЙН РТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ mov si, boot_file_name mov ax, word mov dx, word call find_file mov bx, 0x7E00 / 16 call load_file_data ; РЕТЕІПДЙН ПРО РТПДПМЦЕОЙЕ jmp boot2; рХУФПЕ РТПУФТБОУФЧП Й УЙЗОБФХТБ rb 510 - ($ - $$) db 0x55,0xAA; дПРПМОЙФЕМШОЩЕ ДБООЩЕ ЪБЗТХЪЮЙЛБ load_msg_preffix db "Loading "",0 load_msg_suffix db "". ..",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Starting 16 bit kernel...",13,10,0 start32_msg db "Starting 32 bit kernel. ..",13,10,0 ; mov ax, si pop si ret 'БЗТХЪЛБ ЖБКМБ У ЙНЕОЕН DS:SI Ч ВХЖЕТ BX:0. msg_suffix call write_str pop si push si bp mov dx, word mov ax, word @: push ax call split_file_name mov bp, ax pop AX call find_file test byte, 1 jz @f mov si, bp mov dx, word mov ax, word jmp @b @ : call load_file_data mov si, ok_msg call write_str pop bp si ret ; 0x1000 / 16 call load_file ; mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config_end cmp al, 10 je . cmp byte, 0 je .parse_line; рХУФБС УФТПЛБ cmp byte, "#" je .parse_line; лПННЕОФБТЙК cmp byte, "L" je .load_file ; 'БЗТХЪЛБ ЖБКМБ cmp byte, "S" je .start; 'БРХУЛ СДТБ; ОЕЙ'ЧЕУФОБС ЛПНБОДБ mov al, mov [.cmd], al call error db "Змінити boot script command "" .cmd db ? db ""!",13,10,0 .config_end: ; рТЙ РТБЧЙМШОПН ЛПОЖЙЗХТБГЙПООПН ЖБКМЕ НЩ ОЕ ДПМЦОЩ УАДБ РПРБУФШ; 'БЛУЧЕНІЙО jmp reboot ; 'БЗТХЪЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 mov ax, bx mov cx, 16 mul cx mov word, dx mov word, 0 mov word, 0 pop ax shr ax, 9 - 4 add bx, ax add bp, 16 pop dx jmp .parse_line ; 'БРХУЛ СДТБ.start: ; рТПЧЕТЙН, ЮФП ЪБЗТХЦЕО ІПФС ВЩ ПДЙО ЗБКМ cmp bx, 0x9000 / 16 ko @f 'БРПМОСЕН РПУМЕДОЙК ЬМЕНЕОФ УРЙУЛБ ЖБКМПЧ xor ax, ax mov cx, 16 mov di, bp rep stosw; РЕТЕІПДЙН Л РТПГЕДХТЕ ЙОЙГЙБМЙЪБГЙЙ СДТБ ДМС ОХЦОПК ТБЪТСДОПУФ Inc si cmp word, "16" .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je ; оейъчеуфобс ТСЪТСДОПУФШ СДТБ call error db "Invalid start command argument", 13,10,0; 'БРХУЛ 16-ТБЪТСДОПЗП СДТБ.start16: mov si, start16_msg mov bx 0x6000 mov dl jmp 0x9000 ; 'БРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П Ь БРХУЛІ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; РТПЧЕТЙН, ЮФП РТПГЕУУПТ ОЕ ЇХЦЕ i386 mov ax, 0x7202 push ax popf pushf pop bx cmp ax, bx = 13,10,0 @: ; 'БЗТХЬЙН ЪОБЮЕОЙЕ Ч GDTR lgdt; 'БРТЕФЙН РТЕТЩЧБОЙС cli ; РЕТЕКДЕН Ч 'БЕЙЕЈООЩК ТЕЦЙН mov eax, cr0 or eax, 1 mov cr0, eax ; РЕТЕКДН ПРО 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF; CODE-8 dq 0x00CF92000000FFFF; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32; 32-ВЙФОЩК ЛПД use32 start32: ; ОБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax movzx esp, sp; чЩЧПДЙН УЙНЧПМ ПРО ЬЛТБО mov byte, "! " ;

Для того, щоб писати операційну систему, потрібно розбиратися в багатьох деталях. Ось давайте я вас трохи просвітлю, (але давайте домовимося, що мани ви читатимете самі, щоб було про що поговорити).
Чесно кажучи, на просторах мережі є хмара гладка матеріалів по PM, та й iley і pehat дещо розповіли про цей режим, але мене попросили все одно описати у загальних рамках його. Зараз коротко видам теорію (взагалі спеціально для цього Intel мани писала), потім почнемо писати код.

Введення у захищений режим.
Отже, PM значно відрізняється від всього звичного ще з часів DOS'a реального режиму (RM). Тепер доведеться звикати: тут немає статичних, 64 кілобайтних сегментів, таблиці переривань в 1'ом кілобайті, адрес баз сегментів у сегментних регістрах, загалом зовсім новий світ.
Тепер сегменти описуються в Global Descriptor Table (GDT). Ця таблиця може бути лише в одному екземплярі. Вона є структурою в пам'яті. Чи не сегмент! Може розташовуватися в пам'яті будь-де, але її адреса і ліміт записуються в регістр GDTR. Ось його структура:

Сама таблиця складається із записів наступної структури (до речі нульовий запис порожній. Це важливо. При зверненні до пам'яті, що 'описується' нульовим дескриптором, отримайте #GP – General Protection Fault):
Давайте розглянемо цю структуру уважніше.

1. Segment Limit:
Призначення цього поля відоме за назвою, але є тонкість. Собака заритий у биті G (Granularity).
Якщо вона невстановлена, то пам'ять 'відраховується' в байтах. У такому разі розмір сегмента може змінюватись від 1 байта до 1 мегабайта на розмір в 1 байт.
Якщо встановимо його в 1, буде введена сторінкова адресація пам'яті. Тоді ми зможемо адресувати від 4 кілобайт до 4 гігабайт оперативної пам'яті зі зміною розміру на 4 кілобайти (розмір сторінки). Взагалі сторінкова адресація краща (порівняйте (1Мб+64Кб-16байт) і 4Гб). Давайте в цьому пості поговоримо лише про сегментну адресацію. Paging заслуговує на окрему розмову.

2. Base Address:
Тут вказуємо фізичну адресу бази.

3. Type field:
Комбінації бітів визначають тип сегмента:

4. S (descriptor type):
У документації інтелівської сказано, що якщо цей біт не встановлений, цей дескриптор для системного сегмента, інакше – коду або даних. Під системним мається на увазі LDT, TSS, Interrupt Gates та інші з ними (про них пізніше).

5. DPL (Descriptor Privilege Level):
Привілеї описуваного сегмента. Всім знайомі Rings.

6. P (segment present):
Якщо цей біт встановлений, то процесор 'знає', що сегмент уже пам'яті (хоча краще сказати валідний). Якщо завантажите в сегментний регістр селектор дескриптора з невстановленим бітом P, то буде виняток #NP (not present). Взагалі сенс цієї хитромудрої фрази поясню трохи пізніше.

7. D/B:
Для сегментів різного типу по-різному трактується.
1. Для сегментів коду:
32 або 16 бітна довжина ефективної адреси та розмірність операндів.
(1-32; 0-16);
2. Для стеку:
Покажчик стека 32 або 16 біт. (1-32; 0-16);

8. G:
Впливає те що, у яких одиницях (байти, сторінки) вимірюється ліміт сегмента. Взагалі Paging можна включити під час переходу в PM, встановивши 31 біт регістра CR0.

Ще трохи інформації:
Здогадуємося, що слово Global поставили недаремно. Значить, є ще якась табличка. Мабуть, є також Local Descriptor Table. Їх може бути безліч. Наприклад вони можуть використовуватися для реалізації завдань і.т.д. А от LDTвже є сегментом! Тож звикайте до фраз типу «дескриптор сегмента локальної таблички дескрипторів».

Після того, як ми описали таблицю, потрібно їй завантажити в регістр GDTR. Це робиться далеко не mov'ом. GDTRзаповнюється командою lgdt fword (значення). Тобто треба сформувати самостійно цю структуру та завантажити у вищезгаданий регістр. Є ще команди роботи з цим регістром, але ми мчить галопом Європами.

Ще один момент. У PM в сегментних регістрах зберігаються не базові адреси сегментів (як у RM), а спеціально навчені штуки під назвою селектори. Їхня структура така:

Тут Index – порядковий номер дескриптора у таблиці.
TI показує де шукати дескриптор (у GDTабо LDT).

Тепер коли вже зрозуміло як будувати таблицю, поговоримо про те, як перейти в PM (зауважу, це можна зробити тільки з RM). Взагалі … необхідно встановити біт 0 керуючого регістра CR0. Хоча брешу. Для початку потрібно заборонити усі переривання ( NMI (Non Maskable Interrupts) у тому числі), відкрити адресну лінію A20(щоб була доступна 32-бітна адресація), завантажити GDTRі стрибнути на мітку – старт.

Давайте скористаємося завантажувачем (можна KOLIBRI'ський взяти), який вантажитиме наш код за адресою 1000h:0 (RM'овський, зауважу, адресу).
Тут буде не все так гладко, як у тих манах, коли в PM переходять прямо з бутлоадера. Все трохи складніше. Але спочатку давайте розберемо код, який бутлоадер завантажуватиме (все пишемо на FASM'е). Це своєрідний helloworld. Завантажимося, перейдемо в PM і надрукуємо вітання. Все.

Формат binary
xor ax,ax
cli ;реініціалізуємо сегментні регістри
mov ss,ax
xor sp,sp
sti
mov ax,3
int 10h

Jmp 1000h:r_start

Mov ax,1000h;переналаштовуємо регістри
mov ds,ax
mov es,ax

In al, 0x92;включаємо A20
or al, 2
out 0x92, al

Lgdt fword ;завантажуємо регістр GDTR
mov eax,cr0
or al, 1; встановлюємо 0-й біт
mov cr0,eax;включаємо PM

Jmp fword 08h: Startup32; стрибаємо в PM

Align 8; процесор швидше поводиться з вирівняною табличкою
GDT:
dq 0; порожній
db 0FFh,0FFh,0,0,0,9Ah,0CFh,0; код
db 0FFh,0FFh,0,0,0,92h,0CFh,0;дані
db 0FFh,0FFh,0,80h,0Bh,92h,40h,0 ;відеосегмент
label GDT_SIZE на $-GDT
GDTR:
dw GDT_SIZE-1
dd GDT+10000h
; потрібно записати 32-бітну адресу. Зараз ми знаходимося в сегменті 1000h, база якого 1000h * 10h (за фізичною адресою) => фізична адреса GDTR (мітки!) = 10000h (фізична адреса бази сегмента) + offset

Virtual ;тепер, фактично, забиваємо простір до кінця сегмента
rb 10000h-$;
end virtual
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PM32 Entry;;;;;;;;;;;;;;;;; ;;;;;
use32
org $+10000h;ось для чого: в PM ми працюємо з Flat-сегментами, і якщо ми залишимо код;для PM перед org'ом, то;внутрішньосегментна адреса не співпадатиме з Flat-адресою. Так ось.

Startup32: ;точка входу в PM
mov ax,10h ;тут пишаємо селектори. Найчастіше (! не забуваємо про порядковий номер у
mov es, ax; таблиці) селектор сегмент коду - 08h. даних - 10h, відеосегмент - 18h
mov ds,ax
mov fs,ax
mov ss,ax
mov esp,10000h;стек
mov ax,18h
mov gs,ax

Mov esi,hi_string ;покажемо, що ми вдало перейшли
call print
jmp $

;ESI - адреса рядка
print:
pushad
xor ebx, ebx
mov ah,07h;атрибут
puts:
mov al,
mov, ax
inc ebx
test al, al
jnz puts
popad
ret
hi_string db 'Welcome to PM, dude',0

Що ми зробили? Завантажувач успішно завантажив нас за адресою 1000h:0, звідки ми і продовжили виконання. Спочатку увімкнули А20, заборонили всі переривання, завантажили в GDTRпотрібне значення, стрибнули на мітку входу. Зауважу, що ми стрибали на
jmp fword 08h:Startup32
Тобто 08h – селектор дескриптора коду. Звикайте.

Тепер як це диво запустити. Особисто я користуюсь WinImage та VirtualBox. Запихаємо завантажувач у бутсектор дискети і кладемо.bin'івський файл у корінь. Зберігаємо в.vfd, прописуємо шлях до образу дискети у властивостях віртуальної машини, запускаємо та бачимо результат.

У наступному випуску розглянемо interrupts, faults, traps, aborts та як вони працюють, ловляться та налагоджуються. Почнемо говорити про архітектуру.

Джерела інформації.
1) Відразу хочу висловити подяку Phantom_84 aka egos за те, що вказав на істинний шлях і допоміг мені на самому початку. Без нього мені було б важче розібратися.

Усі процесори Intel, Починаючи з i80286 і до останніх включно, по включенню електроживлення (після початкового «скидання») працюють у режимі реальної адреси (реальному режимі). Зазвичай реальний режим використовується як проміжний для переходу в захищений режим після ініціалізації мікропроцесорної системи, або для більш швидкого виконання програм, написаних для мікропроцесорів 8086 , 80186 , але, порівняно з 8086 , 80186 , Сучасні мікропроцесори в реальному режимі мають більш широкий набір команд і можливість обробки 32-розрядних операндів.
Перемикання процесора в захищений режим із реального здійснюється завантаженням у CR0(рис. 1) слова з одиничним значенням біта РЕ ( Protect Enable). Для сумісності з 80286 біт РЕ може бути встановлений також інструкцією LMSW. До перемикання пам'яті повинні бути проініціалізовані необхідні таблиці дескрипторів IDTі GDT. Відразу після ввімкнення захищеного режиму процесор має CPL = 0.

Мал. 1

Для всіх 32-розрядних процесорів рекомендується виконувати наступну послідовність дій для перемикання в захищений режим:
1. Заборонити переривання, що маскуються, скиданням прапора IF, а виникнення немаскованих переривань блокувати зовнішньою логікою Програмний код на час «перехідного періоду» повинен гарантувати відсутність винятків і не використовувати програмних переривань. Ця вимога викликана зміною механізму виклику обробників переривань.
2. Завантажити у GDTRбазова адреса GDT(Інструкцією LGDT).
3. Інструкцією MOV CROвстановити прапор РЕ, і якщо потрібне сторінкове управління пам'яттю, і прапор PG.
4. Відразу після цього має виконуватися команда міжсегментного переходу ( JMP Far) або виклику ( CALL Far) для очищення черги інструкцій декодованих у реальному режимі та виконання серіалізації процесора. Якщо вмикається сторінкове перетворення, коди інструкцій MOV CROі JMPабо CALLповинні знаходитися у сторінці, на якій фізична адреса збігається з логічною (для коду, якому передається управління, ця вимога не пред'являється).
5. Якщо планується використання локальної таблиці дескрипторів, інструкцією LLDTзавантажити селектор сегмента для LDTу регістр LDTR
6. Інструкцією LTRзавантажити в регістр завдань селектор TSSдля початкового завдання захищеного режиму.
7. Перезавантажити сегментні регістри (крім CS), вміст яких ще належить до реального режиму, або перейти або виклик іншого завдання (при цьому перезавантаження регістрів відбудеться автоматично). У сегментні регістри, що не використовуються, завантажується нульове значення селектора.
8. Інструкцією LIDTзавантажити в регістр IDTRадреса та ліміт IDT- Таблиці дескрипторів переривань захищеного режиму.
9. Дозволити апаратні переривання, що маскуються і не маскуються.

Ця стаття є деяким введенням у розробку програм, що функціонують у захищеному режимі процесора. Тут будуть поставлені основні завдання будь-якої програми, яка працює в захищеному режимі, та наведено їх вирішення. В основному, програми будуть написані мовою FASM.

Режими роботи процесора Intel 80386

З появою процесора Intel 80386виникла архітектура IA32. Вона передбачала появу нового режиму роботи процесора – захищеного (" Protected Mode"). Для сумісності з попередніми процесорами лінійки Intel 80x86процесор 80386 не запускався відразу в захищеному режимі, а працював у так званому реальному режимі (" Real ModeКрім цього, у кожного режиму є один або кілька підрежимів. Розберемо їх.

Реальний режим

У цьому режимі процесор перебуває відразу після натискання кнопки " Power"Комп'ютера. Доступ до пам'яті в реальному режимі здійснюється конструкцією" сегмент:зміщення", яка описує логічну адресу. Значення сегмента, як і усунення, лежить у межах від 0 до 0FFFFh.

Оскільки адресуватися можна лише в межах одного сегмента, то максимальний розмір сегмента дорівнює 64 кілобайт. Фізична адреса, яка виставляється на адресну шину процесора, вважається за формулою:

лінійна адреса = сегмент * 16 + усунення

У реальному режимі процесорів 80186 і 8086 значення сегмента лежало в межах від 0 до 0F000h. Таким чином, максимальна виставлена ​​адреса на адресну шину дорівнює 0FFFFFh, що відповідає (2^20)-1 , тобто. 1 мегабайт.

Звичайно, спочатку обсяг такої пам'яті здавався колосальним, але згодом одного мегабайта стало не вистачати. З появою процесора 80286 став доступний так званий блок пам'яті UMB, що починається з адреси 0FFFFh:0010hі закінчується адресою 0FFFFh:0FFFFh(65 520 байт за межами одного мегабайта). Тепер можна було переконфігурувати операційну систему MS-DOSтак, щоб вона займала цей блок, звільняючи в оперативній пам'яті 64 кілобайти.

Захищений режим

Цей режим має складну конструкцію проти реальним. Логічна адреса є конструкцією " селектор: зміщення". Селектор знаходиться в межах від 0 до 0FFFFh(насправді, селекторів у 4 рази менше – про це докладніше у наступних статтях). Зміщення, на відміну реального режиму, є 32-разрядным, що дозволяє адресувати сегменти розміром 4 гігабайт. Логічна адреса перетворюється на лінійну за наступною схемою:

лінійна адреса = база сегмента + усунення

Лінійна адреса надалі виставляється на адресну шину, якщо не ввімкнено режим сторінкової адресації. В іншому випадку лінійна адреса перетворюється на фізичну, і тільки після цього виставляється на адресну шину. Крім цього, захищений режим дозволяє організувати віртуальну пам'ять, що досягає розміру до 64 терабайт і залежить лише від об'єму жорсткого диска (наприклад, той же файл підкачування в Windowsреалізує віртуальну пам'ять). У захищеному режимі функціонують майже всі сучасні операційні системи.

Мультизадачний режим захищеного режиму

Цей режим дозволяє організувати мультизадачність, тобто можливість одночасного виконання кількох завдань або розрахованої на багато користувачів системи.

Режим віртуального 8086

Це також підрежим захищеного режиму, який дозволяє створити віртуальну машину, яка функціонує начебто вона перебувала в реальному режимі, але, насправді працює в захищеному режимі.

Нереальний режим

Це особливий підрежим реального режиму. Процесор перебуває у реальному режимі, але адресується пам'яті шляхом конструкції " селектор: зміщенняТаким чином доступна пам'ять вище 1 мегабайта. Надалі розглядатиметься захищений режим та його підрежими.

Перша програма: перехід у захищений режим

Перехід у захищений режим здійснюється установкою біта 0 регістра CR0. Перехід у реальний режим здійснюється скиданням того ж нульового біта. Розглянемо програму, яка виконує цю операцію (мова - Flat Assembler):

use16 ; Використовуються 16-розрядні команди

org 100h

Start:

; Ми знаходимося в реальному режимі

mov eax, cr0 ; Зчитуємо значення регістра CR0
or al, 1 ; Встановлюємо нульовий біт
mov cr0, eax ; Записуємо нове значення CR0

; Ми в захищеному режимі

mov eax, cr0 ; Зчитуємо значення CR0
and al, 0feh ; Скидаємо нульовий біт 0
mov cr0, eax ; Переходимо у реальний режим

; Ми в реальному режимі

ret ; Виходимо із програми

Однак ця програма є зовсім "сирою", тому що в ній не можна реалізувати зациклювання. Якщо написати команди начебто hltабо jmp $, то після спрацювання першого ж переривання комп'ютер перезавантажиться.

У нашому випадку команди спрацьовують досить швидко, але не виключено, що в проміжку між виконанням будь-яких команд нашої програми переривання все-таки спрацює, що призведе до моментального збою та перезавантаження. Тому слід подбати про переривання. Отже, побачимо ще раз лістинг. Це не можна назвати першою програмою (скоріше вона пішла б за нульову), оскільки в ній не реалізуються основні дії з переходу в захищений режим. Щоб повноцінно перейти в захищений режим із мінімальними налаштуваннями, потрібно виконати такі дії:

1. перевірити, чи можна перейти в захищений режим;

2. ініціалізувати таблиці дескрипторів;

3. заборонити переривання (як маскувані, так і не маскувані);

4. відкрити лінію A20;

5. завантажити регістри керування пам'яттю;

7. виконати перехід на 32-бітовий сегмент коду, перевизначивши регістр CS.

Але першій програмі достатньо виконати дії 3, 4, 5. Тоді її зациклювання не призведе до перезавантаження комп'ютера. Розберемося у кожному дії.

Заборона переривань захищає нас від перезавантаження. Переривання діляться як на маскувані, так і не маскуються. Щоб заборонити переривання, що маскуються, треба скинути прапор IFрегістра EFLAGSкомандою cli, дозвіл же переривань виконується командою sti. Переривання, що не маскуються, забороняються дещо інакше. Для цього існують два способи: програмування регістрів контролера переривань (цей спосіб буде розглянуто трохи пізніше) або зміна сьомого біта порту 70h: якщо біт встановлений, переривання заборонені, якщо біт скинутий - переривання можуть виконуватися.

Тепер поставимо питання, в чому полягає функція лінії A20, і що це таке. Лінія A20- Одна з 32 адресних ліній. Під час завантаження комп'ютера лінія A20закрито. Це призводить до генерації 20-розрядних адрес (тобто весь адресний простір виходить рівним (2^20)=1 мегабайт). Введено це для сумісності із процесором 8086 : таким чином, намагаючись записати за лінійною адресою 12345678h, ми насправді запишемо за адресою 00045678h, що може призвести до несподіваного результату. Тому для повноцінного функціонування 32-розрядної програми лінія A20обов'язково має бути відкритою. Здійснюється це установкою біта 1 порту 92h, закриття лінії A20- Скидання цього біта.

З останньою дією читач вже знайомий, і вона вже не повинна викликати в нього запитань.

Отже, розглянемо лістинг нашої нової, першої, програми, в якій вже реалізується невеликий цикл. Рядки, які додані до попереднього лістингу, позначені зірочкою (*).

org 100h

Start:

; Ми знаходимося в реальному режимі

cli ;*

in al, 70h; *
or al, 80h; *
out 70h, al; *

; відкрити лінію A20

in al, 92h; *
or al, 2; *
out 92h, al; *

mov eax, cr0
or al, 1
mov cr0, eax


; Невеликий подвійний цикл

mov cx, 20; *

cycle: ;*
mov ax, cx; *
mov cx, 0ffffh; *
loop $ ;*
mov cx, ax; *
loop cycle ;*

mov eax, cr0
and al, 0feh
mov cr0, eax

; Закрити лінію A20

in al, 92h; *
and al, 0fdh; *
out 92h, al; *

in al, 70h; *
and al, 7fh; *
out 70h, al; *

sti ;*

ret ; завершити програму

Переконатися в тому, що програма працює, можна, запустивши виконуваний файл із чистого MS-DOS. Якщо програма коректно завершується, то все гаразд.

Однак можуть виникнути й такі проблеми:

1. комп'ютер зависає";

2. комп'ютер перезавантажується.

Це може виникнути через такі причини:

1. програму запущено в режимі V86(режим віртуального 8086 );

2. програму запущено в захищеному режимі або під конкретною операційною системою.

Тому перед запуском будь-якої програми, яка працює у захищеному режимі, слід перевірити можливість подальшої роботи програми. Про це буде розказано далі.

Перевірка можливості переходу в захищений режим

У попередньому розділі у нас виникла наступна проблема: програма не розпізнає те, що вона знаходиться у захищеному режимі чи режимі V86що призводить до зависання системи або її перезавантаження. Якщо ж ми спробуємо запустити програму під керуванням операційної системи Windows, то Windowsвідловить спробу програми перейти в захищений режим та запропонує перезавантажитись у режимі емуляції MS-DOS(Для платформи 9x), або завершить роботу програми примусово (платформа NT).

Отже, для перевірки того, що ми справді перебуваємо в реальному режимі, слід виконати такі операції:

1. перевірити нульовий біт регістру CR0;

2. Перевірте, чи не завантажено операційну систему Windows.

Перша операція здійснюється прямим читанням регістру CR0з подальшою перевіркою нульового біта регістру EAX, AXабо AL. Якщо біт не встановлено, ми знаходимося у реальному режимі. Інакше подальше виконання програми стає безглуздим.

Друга дія здійснюється викликом функції 1600hпереривання 2fh. Ця функція дозволяє отримати поточну версію операційної системи Windows. Якщо після виклику функції у регістрі ALміститься нуль, то операційна система не завантажена. В іншому випадку, знову ж таки, нашій програмі безглуздо продовжувати будь-які подальші дії.

Розглянемо приклад наступної програми. Вона є модифікацією попередньої програми, всі нові інструкції позначені зірочкою (*).

Org 100h

Start:

; Налаштувати сегментні регістри

mov ax, cs; *
mov ds, ax; *

; Перевірка того, що ми справді в реальному режимі

mov eax, cr0 ;* перевірка нульового біта
test al, 1; * регістра CR0
jz no_pm; *

mov ah, 09h; * функція DOS 09h
mov dx, pm_msg; * виведення рядка
int 21h; *
ret;* і вийти

no_pm:
; Перевірка: чи не запущено програму під Windows

mov ax, 1600h ;* функція 1600h мультиплексорного
int 2fh ;* переривання - отримати версію Windows
test al, al ;* якщо не 0 - помилка
jz no_windows

; Вивести повідомлення про помилку

mov ah, 09h; *
mov dx, win_msg; *
int 21h; *
ret ;*

no_windows:
; Ми точно знаходимося в реальному режимі
; Заборонити переривання, що маскуються.

; Заборонити переривання, що не маскуються (NMI)

in al, 70h
or al, 80h
out 70h, al

; відкрити лінію A20

in al, 92h
or al, 2
out 92h, al

; Перейти в захищений режим

mov eax, cr0
or al, 1
mov cr0, eax

; Тепер у захищеному режимі
; Невеликий подвійний цикл

mov cx, 20

cycle:
mov ax, cx
mov cx, 0ffffh
loop $
mov cx, ax
loop cycle

; Перейти до реального режиму

mov eax, cr0
and al, 0feh
mov cr0, eax

; Закрити лінію A20

in al, 92h; *
and al, 0fdh; *
out 92h, al; *

; Дозволити переривання, що не маскуються (NMI)

in al, 70h; *
and al, 7fh; *
out 70h, al; *

; Дозволити переривання, що маскуються.

sti ;*

; Ми знову перебуваємо в реальному режимі

ret ; завершити програму

; Повідомлення про помилки

pm_msg: ;*
db "Error: already running in protected mode!$" ;*
win_msg: ;*
db "Error: Microsoft Windows detected!$" ;*
Цей приклад теж поки що не показує реалізацію 32-розрядних команд. Для цього слід познайомитися з матеріалом наступного розділу. Крім цього, приклад має наступний недолік: викликаються функції DOS (int 21h), що вже починає суперечити незалежності нашої програми від операційної системи MS-DOS. Надалі доведеться позбутися використання функцій операційної системи та перейти до використання функцій BIOS. Але поки що досить обмежитися і таким кодом.

В даний час я граю з x86 Assember, щоб покращити свої навички програмування на низькому рівні. В даний час у мене виникла невелика проблема зі схемою адресації у 32-бітному захищеному режимі.

Ситуація така:

У мене є програма, завантажена в 0x7e0, яка перемикає CPU у захищений режим і переходить до відповідної мітки в коді:

[...] code to switch CPU в Protected Mode [...] jmp ProtectedMode [...] bits 32 ProtectedMode: .halt: hlt jmp .halt

Поки що все працює відмінно. Jmp ProtectedMode працює без явного далекого переходу, щоб очистити чергу попередньої вибірки, оскільки ця програма завантажується зі зміщенням 0 (org 0 на початку), змушуючи сегмент коду вказувати на потрібне місце.

Моя поточна проблема полягає в тому, що в ярлику ProtectedMode я хочу перейти до іншої програми, завантаженої з 0x8000 (я перевірив це з дампом пам'яті, функція завантаження працювала правильно і програма завантажувалася правильно до 0x8000) ,

Оскільки CPU тепер знаходиться в ProtectedMode, а не в RealMode, схема адресації відрізняється. ProtectedMode використовує дескриптори для пошуку базової адреси та ліміту в таблиці дескриптора, щоб додати дане зміщення та отримати фізичну адресу (як я зрозумів). Тому перед входом ProtectedMode необхідно встановити GDT.

Мій виглядає так:

%ifndef __GDT_INC_INCLUDED__ %define __GDT_INC_INCLUDED__ ;********************************* ;* Global Descriptor Table (GDT) * ; ********************************* NULL_DESC: dd 0 ; null descriptor dd 0 CODE_DESC: dw 0xFFFF; limit low dw 0; base low db 0; base middle db 10011010b; access db 11001111b; granularity db 0; base high DATA_DESC: dw 0xFFFF; data descriptor dw 0; limit low db 0; base low db 10010010b; access db 11001111b; granularity db 0; base high gdtr: Limit dw 24; length of GDT Base dd NULL_DESC; base of GDT %endif ;__GDT_INC_INCLUDED__

і завантажується в регістр GDT через

Lgdt

Те, що я досі не зрозумів, як мені тепер перейти на фізичну адресу 0x8000 у ProtectedMode за допомогою GDT?

Мої перші думки полягали в тому, щоб вибрати дескриптор коду (CODE_DESC), який повинен вказувати на 0x7e00 (були завантажені поточні програми) та використовувати зсув, необхідний для отримання 0x8000 (512 байт), в результаті чого команда переходу:

Jmp CODE_DESC:0x200

Але це не працює.

Jmp 0x7e0:0x200

теж не працює...

Ви хоч уявляєте, що мені тут не вистачає? Можливо, я не зрозумів чогось суттєвого в схемі адресації 32-біт ProtectedMode та використання GDT.

Повний код:

Bits 16 org 0; loaded with offset 0000 (phys addr: 0x7e00) jmp Start Start: xor ax, ax mov ax, cs mov ds, ax ; update data segment cli; clear interrupts lgdt; load GDT from GDTR (see gdt_32.inc) call OpenA20Gate ; open the A20 gate call EnablePMode; jumps to ProtectedMode ;****************** ;* Opens A20 Gate * ;****************** OpenA20Gate: in al, 0x93; switch A20 gate via fast A20 port 92 or al, 2; set A20 Gate bit 1 and al, ~1; clear INIT_NOW bit out 0x92, al ret ;************************** ;* Enables Protected Mode * ;******** ****************** EnablePMode: mov eax, cr0 або eax, 1 mov cr0, eax jmp ProtectedMode ; this works (jumps to label and halts) ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work ;jmp 08h:ProtectedMode , => does not work ;*************** ;* data fields * ;* &includes * ;******** ******* %include "gdt_32.inc" ;****************** ;* Protected Mode * ;*********** ******* bits 32 ProtectedMode: ;here I want to jump to physical addr 0x8000 (elf64 asm program).

11