В марте я занялся серией постов о разработке загрузчика для флешки. Было проделано много предварительной работы.
- Разработка загрузчика с флешки (начало)
- Вывод содержимого регистров в загрузчике
- Чтение/запись физических секторов диска под Windows
- MBR загрузчик в режиме LBA
- MBR загрузчик в режиме CHS
- Чтение FAT32 на С под Windows
- Чтение FAT32 на С под Windows, продолжение
- Чтение FAT32 под Windows на Си (продолжение 2)
- Чтение FAT32 под Windows на C (заключение)
Пришла пора написать, наконец, код. Когда приступил к написанию в понедельник, столкнулся с проблемой долгого отсутствия опыта в ассемблере и с одной элементарной проблемой боролся два вечера. Точнее две ночи. Вчера в два ночи проблему таки поборол. Надобавлял в код отладки и … сидел перед бегущими значениями регистров… Помогло… Сегодня вечером дописал. Так что продолжение ниже.
Ядро загружается по адресу 0x10000. Я использовал такой адрес просто потому, что уже загружал ядро по этому адресу в загрузчике с FAT12.
Нижеприведенный boot-loader загружает ядро из файла ‘KERNEL’ из корневого директория FAT32 флешки.
section .text use16 org 0x7C00 ; Boot Loader starts at 0:0x7c00 start: jmp boot_code db 0x90 BS_OEMName: times 8 db ' ' BPB_BytsPerSec: ; 0x0b Bytes per sector 512,1024,2048 or 4096 dw 0x200 BPB_SecPerClus: ; 0x0d Sectors per Cluster db 0 BPB_RsvdSecCnt: ; 0x0e Reserved sectors count dw 0 BPB_NumFATs: ; 0x10 FATs number db 0 BPB_RootEntCnt: ; 0x11 (not used for FAT32) dw 0 BPB_TotSec16: ; 0x13 (not used for FAT32) dw 0 BPB_Media: ; 0x15 (not used) db 0 BPB_FATSz16: ; 0x16 (not used for FAT32) dw 0 BPB_SecPerTrk: ; 0x18 (SPT geometry for CHS) dw 0 BPB_NumHeads: ; 0x1a (HPC geometry for CHS) dw 0 BPB_HiddSec: ; 0x1c (Sectors before this partition) dd 0 BPB_TotSec32: ; 0x20 (Total sectors must be !=0 for FAT32) dd 0 BPB_FATSz32: ; 0x24 Sectors count in one FAT dd 0 BPB_ExtFlags: ; 0x28 See Microsoft document dw 0 BPB_FSVer: ; 0x2a Version of FAT32 filesystem dw 0 BPB_RootClus: ; 0x2c Root Directory cluster number dd 0 BPB_FSInfo: ; 0x30 Reserved = 1 usially see doc... dw 0 BPB_BkBootSec: ; 0x32 Sector in reserve area with Boot Sect Copy dw 0 BPB_Reserved: times 12 db 0 BS_DrvNum: ; 0x40 Drive number for int 0x13 db 0 BS_Reserved1: ; 0x41 db 0 BS_BootSig: ; 0x42 0x29 means that next fields present db 0 BS_VolID: ; 0x43 Volume ID 32 bit value dd 0 BS_VolLab: ; 0x47 times 11 db ' ' BS_FilSysType: ; 0x52 "FAT32 " db 'FAT32 ' ; parameter block for read sector read_sector_data: ; 0x5a dw 0x10 sectors_count: dw 0x01 read_offset: dw 0x00 read_segment: dw 0x80 lba_lo: dw 0x00 lba_hi: dw 0x00 lba_hihi: dw 0x00 dw 0x00 ; kernel file name kernel: db 'KERNEL ' boot_code: ; 0x5a boot code start cli xor ax, ax mov ss, ax mov sp, start ; set stack pointer and segments mov bp,sp mov ds, ax mov es, ax sub sp,0x80 ; reserve 0x80 byte for local variables sti ; get cluster chain starting from specified push dx ; [ bp - 82 ] BIOS disk number get_first_data_sector: mov eax, [ bp + BPB_FATSz32 - start ] movzx ecx, byte [ bp + BPB_NumFATs - start ] mul ecx movzx ecx, word [ bp + BPB_RsvdSecCnt - start ] add ecx, [ bp + BPB_HiddSec - start ] push ecx ; [ bp - 86 ] reserved + hidden add eax, ecx ; here 0x2fc0 push eax ; [ bp - 8a ] first data sector calc_sector_size: movzx ax, byte [ bp + BPB_SecPerClus - start ] push ax ; [ bp - 0x8c ] sectors per cluster mul word [ bp + BPB_BytsPerSec - start ] shr ax, 4 ; here 0x400 push ax ; [ bp - 0x8e ] segment increment for cluster ; mov bx,ax ; shr eax,16 ; call print_regs ; jmp $ mov bx, search_kernel_file call read_cluster_chain jc error ; we have the kernel here ; initialize segment where to write kernel data ; kernel will be loaded to position 0:0x10000 ; (just after the bool sector) mov word [ bp + read_segment - start ], 0x1000 ; kernel start segment mov al, [ bp + BPB_SecPerClus - start ] mov [ bp + sectors_count - start ], al ; number of sectors to read ; only one iteration is required ( reading whole cluster by one operation) mov byte [ bp - 0x8c ], 0x1 ; number of iterations in read_cluster_chain mov bx, read_kernel_file read_cluster_chain: mov eax, [ bp + BPB_RootClus - start ] ; current cluster to read cmp eax, 0xffffff7 jl rcc_continue ;;;; ;;;; kernel is loaded if bx == read_kernel_file cmp bx, read_kernel_file jne error ; mov ax, 0xCAFE ; call print_regs ; switch to protected mode ; and jump to kernel lgdt [ gdtr ] in al, 0x92 or al, 2 out 0x92, al mov eax, cr0 or al, 1 mov cr0, eax jmp 0x8: _protected use32 _protected: ; jump to kernel jmp 0x8:0x10000 use16 rcc_continue: call get_sector_by_cluster ; number of iterations ; for reading sectors one by one movzx cx, byte [ bp - 0x8c ] ; sectors to read rcc_read_next: call read_sector ; registrers don't restored if error is happen ; we don't need them just show error message ; sorry saving 1 byte 🙂 pusha call bx ; process sector data popa jc rcc_to_next_sector ; processing function finished it's work ; actually it happens only for search directory ; this return can be changed later ret error: int 0x18 rcc_to_next_sector: inc eax loop rcc_read_next ; get next data cluster ; calculate FAT sector to read ; read FAT ; get cluster from FAT mov eax, [ bp + BPB_RootClus - start ] shl eax, 2 ; cluster << 2 mov dx, ax ; lo word shr eax, 16 ; hi word xchg ax, dx ; lo <-> hi mov cx, word [ bp + BPB_BytsPerSec - start ] div cx add eax, [ bp - 0x86 ] ; reserved + hidden push word [ bp + sectors_count - start ] ; sectors to read push word [ bp + read_segment - start ] ; where to read mov word [ bp + read_segment - start ] , 0x80 mov byte [ bp + sectors_count - start ], 0x1 call read_sector pop word [ bp + read_segment - start ] pop word [ bp + sectors_count - start ] mov si , dx mov eax, [ si + 0x800 ] and eax, 0xfffffff mov [ bp + BPB_RootClus - start ], eax jmp read_cluster_chain ; read sector ; input: read_sector_data area ; output: carry flag (if error happen) read_sector: ; read sector lba mode pusha mov [ bp + lba_lo - start ] , eax ; pass LBA address mov si, read_sector_data ; function parameters mov ah, 0x42 ; function number mov dl, 0x80 ; disk number int 0x13 popa jc error ; error happend ret ; search file in directory search_kernel_file: mov di, 0x800 ; [ bp + read_offset - start ] skf_compare_name: mov si, kernel mov cx, 11 push di repe cmpsb pop di jne skf_continue mov cx, [ di + 26 ] mov [ bp + BPB_RootClus - start ], cx mov cx, [ di + 20 ] mov [ bp + BPB_RootClus + 2 - start ], cx clc ret skf_continue: add di, 32 cmp di, 0xa00 jl skf_compare_name stc ret ; calculate sector number by cluster number ; input: eax - cluster number ; output: eax - cluster number get_sector_by_cluster: dec eax dec eax movzx ecx, byte [ bp + BPB_SecPerClus - start ] mul ecx add eax, [ bp - 0x8a ] ; first data sector ret ; This function is called each time when kernel file cluster ; has been read. This function just need to increment the ; memory position (where to read next cluster) read_kernel_file: mov ax, [ bp + read_segment - start ] add ax, [ bp - 0x8e ] ; [ bp - 0x8e ] segment increment for cluster mov [ bp + read_segment - start ], ax stc ret ; alignment times 3 db 0x90 gdt: ; Address for the GDT gdt_null: ; Null Segment dd 0 dd 0 gdt_code: ; Code segment, read/execute, nonconforming dw 0FFFFh dw 0 db 0 db 0x9a db 0xcf db 0 gdt_data: ; Data segment, read/write, expand down dw 0FFFFh dw 0 db 0 db 0x92 db 0xcf db 0 gdt_code16: dw 0FFFFh dw 0 db 0 db 0x9e db 0 db 0 gdt_data16: dw 0FFFFh dw 0 db 0 db 0x92 db 0 db 0 gdtr: dw gdtr-gdt-1 dd gdt finish: times 0x1FE-finish+start db 0 db 0x55, 0xAA ; Boot Sector signature
Протестировался… Работает… Позволю себе совет, тем, кто берётся за такую штуку как загрузчик. Не пишите сразу много кода. Напишите какой нибудь отладочный фрагмент. Например вывод значений регистров (одного регистра) и вставляйте его везде, где можно что-то проверить. Пишите по несколько строк и постоянно тестируйтесь. Я потратил два вечера на отладку элементарной проблемы. Сегодня, написал весь загрузочный сектор часа за 2,5 — 3. Всего сделал 30 последовательных версий. Каждую предыдущую тестировал и продолжал писать от достигнутого. В итоге один вечер и оно работает.
Я использую режим LBA для чтения. Алгоритм использован уже отлаженный на C. Т.е. у меня есть код, читающий цепочку кластеров. В регистре bx я передаю адрес функции, которая вызывается для обработки прочитанного сектора (или секторов). Алгоритм вкратце такой:
- Сначала вычисляются ряд параметров и сохраняются в стеке, чтобы несколько раз не считать.
- Потом читается цепочка кластеров Root Директория. Для прочитанных кластеров вызывается функция поиска ядра в оглавлении
- Потом та же функция чтения цепочки кластеров вызывается для чтения файла ядра, начиная с кластера полученного из элемента оглавления.
- Когда достигается конец цепочки (внутри FAT значение кластера > 0xffffff7), ядро считано.
- Производится переключение в защищённый режим и переход на код ядра уже в защищённом режиме.
О сборке ядра я уже писал ранее. Все ссылки завтра… Сегодня поздно уже…
P.S.
Все ссылки смотрите здесь: Обработка прерываний в ядре ОС на базе IA32
P.P.S.
Позже, я обнаружил, что в инсталляции Операционной Системы Kolibri, большой комплект разных вариантов загрузчиков разработан Евгением Гречниковым (aka Diamond). См. форум http://board.kolibrios.org, а также статью здесь в блоге: Загрузка Kolibri OS с флешки
Очень интересно, столько работы а готовый образ MBR который грузит ядро из файла kernel есть? Готовое решение есть?
Столкнулся что на старых ноутбуках и компах bios глючной и не понимает флешку если она не в zip стандарте, для этого нужно протестировать биос и если он не поддерживает lba эмулировать chs, так вот только один нормально работающий такой MBR нашёл но он грузит только syslinux 3.71 а хотелось бы иметь выбор что грузить. В программировании ноль так что сам исправить MBR не смогу на то чтоб грузил то что хочется. Если есть возможность использовать ваше решение, был бы признателен.
В других статьях есть примеры готовых MBR загрузчиков в CHS режиме и в LBA режиме:
В мои планы не входило делать бинарные инсталляции. Я не ставил целью сделать поддержку всех версий BIOS, проверку работоспособности везде и т.п. Всмысле загрузчики рабочие, но их нужно компилировать, инсталлировать и т.д. Всё описано и выложено, но в исходниках. Моей целью было продемонстрировать внутреннее устройство и сам процесс разработки.
Вам, судя по-всему, нужен готовый продукт. Чтобы ни о чем не думать с инсталлятором и т.д.
Поищите здесь: готовый загрузчик с флешек, автор Сергей Гончаров aka Yoda: http://www.osdev.ru/viewtopic.php?f=4&t=485
Или MBR-загрузчик Alter.
http://forum.osdev.org/viewtopic.php?f=2&t=23227&start=0
Спасибо за ответ обязательно по ссылкам посмотрю, но думаю что там нет того что мне надо. Мне не нужен полностью готовый вариант с инсталляторами и т.п. могу и ручками MBR загрузить вот только компилировать а точней дорабатывать код не смогу. Дело в том что буквально все даже GRUB и SYSLINUX и PLOP и прямой FreeDOS не грузятся именно с USB флешки когда BIOS кривой и не поддерживает на USB LBA адресацию. Вообще очень часто реализация USB в BIOS очень кривая потому как сам интерфейс появился уже не в эпоху DOS а в эпоху форточек, и вся поддержка только через дрова в мастдае.
Я только один MBR нашел который проверяет на наличие поддержки в BIOS LBA и если его нет эмулирует CHS, но он как то странно подгружает ядро syslinux. По идее должен грузить любое ядро записанное в 1 сектор первого партишена с тем же именем , но нет он грузит исключительно версию 3.71 . А она к сожалению глюковатая и не имеет многих более поздних доработок. У меня вообщем то просто не чисто теоретический вопрос или изыскание как вышло у вас, если честно никогда не видел смысла в чистой теории всегда стремлюсь применить в жизни иначе зачем оно? Я собрал пару лет назад флеш на freedos для восстановления и т.п. Но друзья которым я ее дал все время жаловались что и загрузка не 10 сек как у меня и вообще бывает не грузиться, вот дошли руки решил разобраться и понял что виной кривые BIOS, а решение — эмуляция, вот и стоит задача подружить приличный загрузчик ака syslinux или GRUB с эмуляцией CHS для кривых биосов. К моему удивлению такие монстры и не имеют эмуляции и не грузятся на многих компах особенно дешевых мамках ))) В инете полно можно встретить описаний что то проверенный образ Linux то еще какой именитый образ не грузиться все из за этой проблемы USB. Если писать на CD то все как правило ок, там нет такого разнообразия геометрий диска, там стандарт, а на USB черт знает что.
MBR Alter — на сколько я понял обычный загрузчик только с возможностью загрузки по горячей клавиши другого партишена. Не понятно зачем это надо , для скрытой системы и загрузки с нее? Чтобы по горячей клавише загружалась система скрытая от общих глаз? Вобщем про эмуляцию и трансляцию из LBA в CHS ни слова.
А вот набор от Сергея Гончарова очень интересный, и если там нет еще эмуляции я ему намекну что там ей самое место )))
Судя по вашему описанию, у вас есть работающий загрузчик, грузящий версию SYSLINUX 3.71. Другие версии он может не грузить по нескольким причинам. Загрузчик состоит из MBR части и собственно Boot Loader в начале загружаемого раздела. Оба этих компонента должны поддерживать работу в CHS режиме. С версией 3.71 у вас с этим все в порядке. Но есть еще один возможный источник проблем. Собственно само ядро операционной системы. Оно после загрузки само начинает обращаться к файловым системам. Версия 3.71 содержит необходимые драйверы, в более поздних версиях ядра может просто не оказаться нужных драйверов. Кроме того, у более поздних версий может быть ещё одна прозаическая проблема — размер загружаемого ядра. Загрузчик 2-ой стадии может к примеру поддерживать CHS, но не поддерживать больших файлов. Поскольку работает в реальном режиме. Т.е. возможных проблем много.
Мне удалось загрузить на моем ноуте с багом в BIOS (не поддерживает нормально LBA) при помощи MBR Сергея Гончарова GRUB4DOS последней версии по цепочке
MBR Сергея Гончарова -> MBR GRUB -> ядро GRLDR
и после grub получил нормальную геометрию диска и нормально читал и мог грузить что угодно. Сам grub при помощи своего MBR не грузиться на бажном ноуте, только на нормальном ноуте грузиться без проблем. Вот только я все время меняю разбивку флешки и уже не помню как именно она была разбита а на FAT32 4k cluster что то MBR Сергея Гончарова не грузится, вобщем у него хорошая вещь но еще не доработана, не с любой FAT32 будет работать.
Спасибо вам за ссылки и ответы глядишь общими усилиями и будет хорошее решения и не только для прямых BIOS ))