Phoenix BIOS

Kernel boot-loaders discussion
  • theonlymirage wrote:Никакого результата даже если в начало MBR записать что-то вроде этого:
    Про стек, надеюсь, не забыл?
  • Да, в основной программе со стеком всё хорошо, там много чего не работало бы без стека. В минимальном коде чего я уже не делал, который я сейчас мучаю (считай mbr) стек задал так:

    Code: Select all

    mov sp, main+500h ;main указывает на начало кода mbr
    видел делают так:

    Code: Select all

    mov sp, 7C00h ; адрес загрузки кода. Вопрос: так можно, там же до кода биоса должно быть что-то в виде пустой прослойки?
    Как лучше всего? Могу зашить в код, выделить и внутри кода память под стек.
    Если что ss = 0: адресация же ss:sp, верно?

    Плюс нашёл одну ошибку в начале кода, исправил:

    Code: Select all

    mov ax, cs  ;было xor ax, ax
    но на этом биосе всё хорошо (cs = 0).
  • Давайте конкретизирую вопрос неким базовым кодом:

    Code: Select all

    use16
    org 0x7C00
    
    main:
      cli
      mov ax, cs ;xor ax, ax
      mov ds, ax
      mov ss, ax
      mov sp, stack_start ; тут надо исправить глубину стека
      mov bp, stack_start
      sti
      mov si, text
      call put
      cli
      ;hlt
      jmp $
    
    put:
      mov ah, 0x0e
      mov bh, 0
      lodsb
      test al, al
      jz endc
      int 10h
      jmp put
    endc:
      ret
    
      text db 'Hello!', 10, 13, 0
    
    stack_end:
      db 200 dup(0)
    stack_start:
    
      times 510-($-main) db 0
      db 0x55, 0xAA
      
    Помогите исправить все ошибки, из того что я вижу: правильно задать стек, смещения с учётом cs, возможно нужно задать режим отрисовки и очистить экран перед выводом и плюс задать позицию ввода и сегмент.
  • theonlymirage wrote:видел делают так:

    Code: Select all

    mov sp, 7C00h ; адрес загрузки кода.
    Если ss = 0, вполне работающий вариант.
    theonlymirage wrote:Плюс нашёл одну ошибку в начале кода, исправил:

    Code: Select all

    mov ax, cs  ;было xor ax, ax
    но на этом биосе всё хорошо (cs = 0).
    Разные биосы могут запускать загрузочный сектор с адресов либо 0:7C00, либо 7C0:0.
  • Ужас нарастает. Выяснил, что прошивка пытается автоматически определить, какой режим загрузки использовать. Как она это делает:
    -- по первой инструкции (xor ax, ax или jmp 00:main)
    -- по таблице разделов
    -- по области BPB
    -- с помощью иной магии
    От этого может меняться значение в cs и адрес загрузки кода.

    Если в начале jmp, то считается есть область BPB, которую мы перепрыгиваем. Этот переходный BIOS, который одним краем граничит с UEFI, в этом случае вполне может заполнить эту область данными о загрузочном устройстве - затерев наш код.

    Получается, что более менее универсальный MBR в современном мире будет ещё более ограничен в размерах (на целую область BPB, которая не всегда и не всем нужна).
  • Ещё одна моя ошибка:

    Code: Select all

    mov ss, <значение>
    порождает прерывания, это есть в мануалах intel. Такое поведение существует аж с 8088. Команда cli не может его замаскировать. После этой команды не следует класть что-то в регистр sp, что я сделал в коде выше.

    Самая главная ошибка была в другом, я зря терял время, нужно было сбросить флаг направления инструкцией cld. В этих bios'ах он выставлен и происходит декремент при печати строки, а не инкремент.
  • Нашёл аналогичную ошибку в Колибри - внёс изменения на SVN. На моих машинах вывод сообщений от Колибри теперь заработал.
    Молюсь, чтобы нигде не ошибся для других ;)


    Про последовательность инструкций:

    Code: Select all

    mov ss, <значение1>
    mov sp, <значение2>
    не стал править и не обязательно - этого наверное никто не делает и оно везде работает, но следует помнить, что иногда может произойти проваливание в прерывания и никакое cli не поможет.
  • theonlymirage, а это только на Фениксы влияет? Есть где-то информация?
  • Сразу отмечу, что с официальной информацией не густо.
    На опыте, на практике такая проблема возникла только на Фениксах. Конечно, у меня было мало разных железок за всё время, но факт есть факт.

    Есть ABI-стандарт и соглашение, где у всех чётко прописано значение 0 по дефолту и рекомендация обнулять флаг направления при выходе из каждой функции. Об этом есть в System V Intel386 ABI (Registers and the Stack Frame), AMD64 ABI и в других документах и почти на всех форумах. Найти оф. описание, касаемо выставленного флага, Фениксов мне не удалось, зато есть множество тем, где люди с этим сталкивались. Чтобы понять, что проблема длится достаточно давно, я приведу эту ссылку и цитату:
    Years ago, I worked for Tandy when they built their first AT compatible. I had written a disk drive diagnostic that ran fine on a real AT but hung up every time I ran it on our prototype (which worked fine otherwise, including running DOS). We traced the problem to the fact that the BIOS that Tandy licensed from Phoenix (also at an early revision level) used a series of string moves without clearing the direction flag, and my diagnostic happened to set the flag in a display routine. Somebody tried to argue that maybe DOS expected the flag to be cleared at all times. But they couldn't produce any documentation, and besides we could see where the IBM BIOS used a similar series of string moves and did establish the state of the direction flag first, so Phoenix added it. Presumably, Phoenix used a "cleanroom" system: one group of software engineers studied the IBM BIOS source code (which IBM published) and wrote a specification which was then designed and coded by another group of programmers.

    Posted by Ellis Easley on 25 Sept 2003, 5:13 p.m.
    Соответственно нужно смотреть исходники IBM BIOS и ожидать там такой же проблемы. А пока же мой практический опыт показывает: добавляем cld и всё работает, убираем - и ничего (в лучшем случае, экран пустой чёрный с мигающей кареткой ввода).


    Про догадки об умном BIOS'е: https://stackoverflow.com/questions/392 ... 3#39248323
    Так же видел это в каком-то оф. документе, но не сохранил - найду скину ссылку сюда.
    Цитата:
    In theory you wouldn't need a BPB when writing a MBR and not a VBR, and the presence of the xor ax, ax instruction wouldn't influence the booting.
    You should include a xor bh, bh however (more on Int 10/AH=0Eh)

    Sadly this is just theory.

    Specially for USB devices a BPB is implicitly assumed by some firmware, including the full FDC descriptor (with a valid OS name).
    Many thanks to Michael Petch for stressing this out.

    Since the introduction of UEFI implementations, particularly the parts dealing with CSM (Compatibility Support Module), i.e. legacy booting, writing a fully supported MBR has became tricky.

    The firmware will sometimes try to automatically detect what boot mode to use and since all UEFI devices are also legacy devices per specification, the firmware must rely on some quirk to tell them apart.

    My firmware detect a device as "legacy", even when explicitly told so, only when at least one of these is true:

    There is a bootable, non empty, partition in the MBR partition table.
    The starting/ending address, either in CHS or LBA, are not checked at all.
    The first instruction is a xor ax, ax (in either forms: 33 C0 or 31 C0). This is because the first thing most bootloaders do is to set the segment registers to zero through AX.
    There may be other "signatures", like a jump at the first bytes, but I haven't tested them (yet).

    If the firmware fails to detect the device as legacy and it is not a UEFI compliant device, it will be skipped.

    Posted by Margaret Bloom
    По немаскируемым прерываниям и магии при изменении сегментных регистров ещё большая каша. Я просто приведу несколько ссылок:
    Ссылка: https://dev64.wordpress.com/2012/05/14/ ... reference/
    Цитата:
    Дополнительная информация для обработки исключения

    Особенное внимание должно быть уделено тому, чтобы исключение возникающее в процессе явного переключения стека не приводила к использованию процессором invalid stack pointer (CS:ESP). Программы, написанные для 16-ти битных IA-32 процессоров часто используют пару инструкций для переключения на новый стек, например:
    MOV SS, AX
    MOV SP, StackTop
    При выполнении этого кода на одном из 32-битных IA-32 процессоров, возможно получить page fault, general protection fault (#GP) или aligment check fault (#AC) исключения после загрузки сегментного селектора в регистр SS, но прежде загрузки регистра SP значением нового StackTop. В этот момент пара SS:ESP является нецелостной. Новый стековый сегмент, и старое значение ESP (stack pointer).
    ...
    В системах, обрабатывающих page-fault, general-protection или aligment check исключения внутри задачи генерирующей fault (с помощью trap или interrupt gate), программа выполняющаяся на том же уровне привелегий, что и обработчик исключения, должна инициализировать новый стек используя LSS инструкцию, вместо пары MOV инструкций, приведенных выше.
    Ссылка: https://c9x.me/x86/html/file_module_x86_id_304.html
    Ссылка: http://www.vcfed.org/forum/archive/inde ... 41453.html

    Ссылка: https://web.itu.edu.tr/kesgin/mul06/int ... r/mov.html
    Цитата:
    If the destination is SS interrupts are disabled except on early buggy 808x CPUs. Some CPUs disable interrupts if the destination is any of the segment registers
    Если кратко, то есть около 4 типажей процессоров, которые по-разному относятся к этой последовательности инструкций.
  • Выделил вопрос в отдельную тему
    KolibriOS » Список форумов » Основные темы » Ядро » Загрузчик » Phoenix BIOS
    to infinity and beyond
  • Intel SDM, Vol. 2, Ch. 2, MOV -- Move:
    Loading the SS register with a MOV instruction inhibits all interrupts until after the execution of the next instruction.
    This operation allows a stack pointer to be loaded into the ESP register with the next instruction (MOV ESP, stack-
    pointer value) before an interrupt occurs. Be aware that the LSS instruction offers a more efficient method of
    loading the SS and ESP registers.
    Насколько могу судить, в cli/sti оборачивать не нужно.
  • Не все процессоры ведут себя так. Выше я как раз писал об этом, приводя ссылки.
    Но, конечно же, в целом все придерживаются такого поведения. Особенно если мы говорим о современном железе.

    У меня было время посмотреть другие загрузчики Колибри (самые что называется "боевые" и рабочие), и там всё это есть (и cli, sti и cld) в определённой мере. Ну, например, загрузчик FAT32 с USB.

    Code: Select all

    @@:
            xor     eax, eax
            mov     ds, ax
            mov     ss, ax
            mov     sp, 7C00h
            mov     bp, sp
            mov     [boot_drive], dl
            cld
            sti
    
    К сожалению, я выяснил, что необходимость в cld не единственная проблема для моих Phoenix BIOS при загрузке с USB. Возможно он анализирует что-то ещё, просто записанный MBR на флешку не работает, нужна структура файловой системы/разделов. Например, такая же разметка FAT32, как у загрузчика в примере выше - тогда всё работает.
    Короче, пока я не разобрался со всеми нюансами. Думаю есть люди, которые знают больше и лучше расскажут об этом.

    P.S. Если экономить место на байтах загрузчика, то да, не нужно и до этого работало на большинстве железок. Кстати, для экономии можно попробовать пожать загрузчик аналогом kpack. Ахахах... я где-то такое видел =)
  • Who is online

    Users browsing this forum: No registered users and 2 guests