LINUX.ORG.RU

Ассемблер в среде BIOS. Передача управления

 ,


0

1

Доброго времени суток!

Пишу загрузчик на языке GNU Assembler-а. Загрузчик своеобразный... Его основная работа - загрузить оригинальный загрузчик и передать ему управление. Для этого, я подменяю оригинальный загрузчик своим (записываю в первый сектор раздела), а оригинальный сохраняю в другом секторе.

При этом мой загрузчик делает следующие действия:

  1. После загрузки в оперативную память по адресу 0x7C00 он копирует себя же в другое место (например, по адресу 0xA000)
  2. Далее прыгает на этот адрес со смещением (чтобы не выполнять тот же код) и продолжает работу оттуда
  3. Читает раздел с оригинальным загрузчиком в память по адресу 0x7C00
  4. Прыгает по адресу 0x7C00 и передаёт управление оригинальному загрузчику

Проблемы возникают при работе с адресами.

Есть фишка, что адрес рассчитывается относительно соответствующего сегментного регистра по формуле:

реальный_адрес = (сегментный_регистр << 4) + относительный_адрес

Но когда эта формула срабатывает - я не всегда могу понять.

Например, копирование блоков памяти. Вот как я копирую свой загруженный в оперативную память загрузчик.

	movw $0x0, %si		# откуда = DS:SI = (0x07C0 << 4) + 0x0 = 0x7C00
	movw $0x0A00, %ax
	movw %ax, %es		# ES = 0x0A00
	movw $0x0, %di		# куда ES:DI = (0x0A00 << 4) + 0x0 = 0xA000
	movw $0x0200, %cx	# сколько = 512
	rep movsb

По комментариям видно, что я предполагаю, что как адрес «откуда», так и адрес «куда» рассчитывается именно с учётом сдвига значения сегментного регистра.

Далее по команде jmp. Если я не ошибаюсь, то при передаче ей в качестве аргумента имени метки, итоговый адрес перехода рассчитывается как раз относительно значения сегментного регистра кода cs. Тогда, если я обнулю этот регистр, то по сути я могу передать команде в качестве аргумента абсолютный адрес:

	movw $0x0, %ax
	movw %ax, %cs
	jmp 0xA000

Но это не работает. Более того, для меня стало неожиданным, что сработал код:

	jmp 0x0A00

По крайней мере сообщение, выводимое сразу после установки всех сегментных регистров, выводится два раза:

_boot:
# Устанавливаем начальное значение сегментных регистров
	cli
	movb %dl, iBootDrive
	movw $0x07C0, %ax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %ss
	movw %ax, %sp
	sti

	mWriteString msgHello

Кроме этого, меня интересует, как пропустить уже выполненный код. Логичным для меня является что-то вроде этого:

	jmp 0xA000 + continue
	continue:

То есть выполняется прыжок в адрес, равный 0xA000 (там скопированный загрузчик) + значение метки. Вроде бы всё логично, но следующий код опять же просто выводит два сообщения:

	jmp 0x0A00 + continue
	continue:

Как будто просто игнорирует смещение метки и выполняет код заново.

Плюс ко всему, инициализация сегментного регистра кода cs вообще ломает исполнение кода. Даже элементарный вывод строки не может выполниться.

Возможно, следующая информация будет полезна. Собираю загрузчик так:

as boot.s -o boot.o
ld -Ttext 0x0000 --oformat=binary boot.o -o boot.bin

Директиву org не указываю.

Таким образом, у меня вопрос: Как правильно реализовать копирование памяти и передачу туда управления?

Спасибо!

А каким образом ты делаешь инициализацию CS? Его же вообще не трогают, он меняется при всяких call, jmp, ret.

ilovewindows ★★★★★
()
Ответ на: комментарий от ilovewindows

А каким образом ты делаешь инициализацию CS?

Ну вон я там писал:

	movw $0x0, %ax
	movw %ax, %cs
	jmp 0xA000

Его же вообще не трогают, он меняется при всяких call, jmp, ret.

Не знал, что не трогают. А меняется же вроде регистр IP, а не CS. Но я могу ошибаться.

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus

А разве записав в CS адрес нового сегмента кода, ты уже не будешь на следующей инструкции исполнять код по новому сегмент ногу регистру автоматом? Зачем тут вообще прыжок? Типа процессор на каждой инструкции берет адрес из связи CS:IP. Попробуй просто записать в CS свой новый адрес, а дальше уже просто свой код для загрузки стороннего загрузчика

crarkie
()
Ответ на: комментарий от crarkie

P.S Есть еще такое понятие, как короткий прыжок, т.е в пределах сегмента. И дальний прыжок, т.е и в другой сегмент.

crarkie
()
Ответ на: комментарий от AccumPlus

Явно его не загружают (вообще должен орать компилятор) пишут, что-то типа jmp XXXX:YYYY, вот тут уже не помню, это или адрес кода, или ячейка(адрес) с адресом кода jmp dword ptr XXXX:YYYY, в разных компиляторах по разному надо объяснить что это дальний прыжок сегмент+смещение. И перегонять с места на место загрузчик тоже не понимаю зачем. При включении процессора у него в CS:IP что-то по умолчанию на первую инструкцию в ПЗУ биоса, биос грузит с диска загрузчик (в данном случае твой) прыгает на первую твою инструкцию, твоему нужно вызовом биоса int какой-то загрузить сектор со старым загрузчиком в память и передать ему управление, надо смотреть какие регистры для этого int нужны. Или всю хуже и ты биос пишешь?

ilovewindows ★★★★★
()
Последнее исправление: ilovewindows (всего исправлений: 1)
Ответ на: комментарий от crarkie

Попробуй просто записать в CS свой новый адрес

А это логично. Но, к сожалению, не сработало:

	movw $0x0A00, %ax
	movw %ax, %cs

	mWriteString msgHello

Сообщение не вывелось. Тут конечно ещё вопрос, точно ли копирование памяти выполняется так, как я подразумеваю, и по адресу 0xA000 действительно есть команды.

AccumPlus
() автор топика
N_sectors equ 34
    bits    16  
    cpu     286 
    org     0x7c00
    mov     ax,0xb800
    mov     es,ax
    mov     ax,0x0000
    mov     ds,ax
    mov     bx,80
    mov     cx,msg_len
msg_print:
    inc     bx  
    mov     ax,bx
    shl     ax,1
    mov     si,ax
    mov     ax,0x2700
    mov     al,[bx+message]
    mov     [es:si],ax
    loop    msg_print
    mov     ax,0x2700
    mov     [es:1000],ax
    mov     ax, 0x0100
    mov     es, ax
    mov     bx, 0x0 
    mov     ah, 0x02 ;read
    mov     al, N_sectors
    mov     cx, 0x0002
    mov     dh, 0x0 
    int     0x13
    jmp     0x0100:0000
message:
    db      "Try to load vsjakuju hren"
msg_len   equ $-message
    hlt 

и потом

nasm -fbin mbr.s -o mbr.bin
python genMbr.py mbr.bin mbr.ima
cat mbr.ima very_useful_program.bin > build/floppy.ima
где питоновский скрипт генерирует сектор целиком
import sys 

rfile = file (sys.argv[1], "rb")
prog = []
try:
  byte = rfile.read (1) 
  prog.append (byte)
  while  byte != "": 
    byte = rfile.read (1) 
    prog.append (byte)
finally:
  rfile.close()
wfile = file (sys.argv[2], "wb")
print sys.argv[2]
for it in prog:
  wfile.write (it)
for it in range (512 - 1 - len (prog)):
  wfile.write (chr(0))
wfile.write (chr(0x55))
wfile.write (chr(0xaa))

luke ★★★★★
()
Ответ на: комментарий от ilovewindows

Или всю хуже и ты биос пишешь?

Не не, там не должно быть ничего сложного. В будущем раздел будет зашифрован, и мой загрузчик должен будет обрабатывать команды bios для расшифровки (но это гораздо позже). Но загрузка системы остаётся на плечах оригинального загрузчика.

твоему нужно вызовом биоса int какой-то загрузить сектор со старым загрузчиком в память и передать ему управление

Да, всё так. Но оригинальный загрузчик настроен на адрес 0x7C00. Если я его загружу в другое место - скорее всего что-то пойдёт не так. В то же время, свой загрузчик я могу настроить как мне пожелается, но изначально он всё равно будет грузиться в 0x7C00. Именно поэтому я его копирую в другое место и потом гружу оригинальный в 0x7C00.

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus
movw $0x0A00, %ax
pushw %ax
movw continute, %ax
pushw %ax
retf
continue:
// next..

Может как-то так? Давно последний раз писал на асме, могу где-то тупануть

crarkie
()
Ответ на: комментарий от crarkie

Может как-то так?

Красиво! Спасибо!

Вот я ещё нашёл:

Segment-offset pointers are specified using the following format:

jmp $segment, $offset

Это сработало:

jmp 0x0A00, $continue
AccumPlus
() автор топика
Ответ на: комментарий от Unicode4all

Лучше EFI-загрузчик ковыряй. Биос никому не нужен в 2017 году.

Главное знать его управляющие команды. Систему то грузит оригинальный загрузчик.

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus

Только в режиме совместимости. Можно ядро сразу из EFI грузить.

hateyoufeel ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.