Я пытаюсь написать простой загрузчик для архитектуры x86, который должен просто выводить символ «A», входить в защищенный режим, а затем останавливаться. Мой код с комментариями выглядит следующим образом:
BITS 16
ORG 0x7c00
jmp 0:start ;set cs to 0
start:
mov ax,0x7c0
add ax,288
mov ss,ax
mov sp,4096
mov ax,0x7c0
mov ds,ax ;Sets segment descriptors for now
mov ah,0eh
mov al,65
int 10h ;Print A for test
jmp pretect ;Jump to a ~1 second delay before entering protected mode so we can see the 'A' if anything goes wrong
nod:
jmp nod ;Not used for now
gdtstp: ;global descriptor table
dq 0 ;Null
dw 0xffff ;Entry 08h, full 4gb
dw 0
db 0
db 0x9a
db 11001111b
db 0
dw 0xffff ;Entry 16h, full 4gb
dw 0
db 0
db 0x92
db 11001111b
db 0
gdtr: ;descriptor for gdt
dw 24
dd gdtstp
pretect: ;Wait for about 1 second before jumping to protect
mov esi,0x20000000
.loop:
dec esi
test esi, esi
jz protect
jmp .loop
protect: ;attempt to enter protected mode
cli
lgdt [gdtr] ;set gdt register
mov eax,cr0
or al,1
mov cr0,eax ;set bit 1 of cr0
jmp 08h:idle ;sets cs to 08h and jumps to idle
idle:
jmp idle ;Should stop here
times 510-($-$$) db 0
dw 0xaa55 ;magic number
Это в NASM и выполняется на qemu. У меня есть небрежный способ добавить задержку примерно в 1 секунду между выводом «A» и попыткой входа в защищенный режим. В настоящее время, когда я пытаюсь запустить этот код, он печатает букву «A», задерживается примерно на секунду, а затем перезагружается. Я не могу сказать, почему это так, но я предполагаю, что это вероятно потому, что глобальная таблица дескрипторов недействительна или неправильно загружена, или потому что дальний переход для установки селектора сегмента кода неверен.
Мой код должен делать следующее: напечатать 'A', иметь GDT из 3 записей: нулевой дескриптор, сегмент кода всех 4 ГБ и сегмент данных всех 4 ГБ, иметь GDTR, который определяет 24 байта, и адрес GDT, подождите 1 секунду, отключите прерывания, загрузите GDT, включите защищенный режим, установите селектор сегмента кода с большим переходом в состояние ожидания, а затем оставайтесь там на неопределенное время.
Если можно определить, что в моем коде сборки не подходит для входа в защищенный режим, укажите это. Я понимаю, что начальный загрузчик обычно не выполняет эту задачу, но я просто пытаюсь понять, как он работает, с помощью минимально работающей программы.
РЕДАКТИРОВАТЬ: после изменения
mov ax,0x7c0
mov ds,ax
to
mov ax,0
mov ds,ax
и помещая mov ax,08h
между idle:
и jmp idle
, qemu вылетает и дает следующее:
qemu: fatal: Trying to execute code outside RAM or ROM at 0x000a0000
EAX=feeb0010 EBX=00000000 ECX=00000000 EDX=ffffffff
ESI=00000000 EDI=8000007c EBP=00000000 ESP=00000ffc
EIP=0009fc6d EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =08e0 00008e00 0000ffff 00009300 DPL=0 DS16 [-WA]
DS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
FS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
GS =0000 00000000 0000ffff 00009300 DPL=0 DS16 [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c1f 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=feeb0010 CCD=feeb0010 CCO=ADDB
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
ea 5f 7c 08 00
. Инструкцияjmp idle
имеет коды операцийeb fe
и находится в позиции 0x5f в файле, что, как мне кажется, означает, что она загружена в позицию 0x7c5f (куда направляется дальний переход) в основной памяти. - person user2649681   schedule 04.10.2018mov ax, 08h
перед ожиданием jmp. Но вам следует загрузить все сегментные регистры (ES, DS, SS) с помощью селектора 010h (вы только установили для CS значение 08h). Вы также можете установить GS и FS на 010h, если хотите.EIP=0009fc6d
в дампе говорит о том, что вы где-то заблудились в памяти. - person Michael Petch   schedule 04.10.2018jmp 08h:idle
. После установки SS вы также должны установить ESP - person Michael Petch   schedule 04.10.2018int 10h
сah = 0Eh
однократным входом в защищенный режим приведет к его перезагрузке? Я пытаюсь подтвердить, что он достиг этой части кода, поэтому я установил эти селекторы и добавил этот вызов прерывания, но он просто перезагружается - person user2649681   schedule 04.10.20180xb8000
до того, какjmp 08h:idle
печатает символ, но делать это после того, какjmp
не удается? Или даже каким-либо образом я могу проверить, действительно ли код заканчивается там, где он должен быть послеjmp
? - person user2649681   schedule 04.10.2018jmp 08h:idle
иidle:
, потому что эти инструкции никогда не будут выполнены, потому что вы перепрыгнули через них. Сделайте что-нибудь вродеjmp 08h:setcs
setcs:
, затем поместите код для инициализации сегментов и после этого выполните цикл ожидания. - person Michael Petch   schedule 04.10.2018jmp 08h:idle
затем послеidle:
инициализации селекторов сегментов, затем запись в0xb8000
после этого. - person user2649681   schedule 04.10.2018mov ebx,0xb8000 \ mov ax,0x0748 ;value of character \ mov [ebx],ax
Эти три инструкции, как я полагаю, должны записывать 0x0748 в 0xb8000 в памяти с помощью GDT, описанного в OP - person user2649681   schedule 04.10.2018JMP 08h:idle
вам нужно будет убедиться, что у вас есть строкаbits 32
, чтобы с этого момента ассемблер выдавал 32-битные инструкции. - person Michael Petch   schedule 04.10.2018