Перейти до вмісту

"Мій нікому нецікавий блоґ" або записки за UEFI.

GPT UEFI FAT

Повідомлень в темі: 70

#1 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 14.09.2016 – 20:48

  • 65
Шоб не муляло у флуді, і шоб не викинуте було (флуд же чиститься), переніс сюди. Писатиму тут (коротко Зображення) про свої успіхи в справі імплементування UEFI на кількох mips і arm одноплатних компутерах (ОПК).
А почалося з опису простої програми, за допомогою якої, ми створили потрібний розкрій (layout) на SD картці. Яка гратиме роль завантажувального носія на цільових машинах (ця розповідь про створення цього на одній з цільових машин, а саме платі Beagle Bone Black, але подібні форматування нам треба буде робити для кожної машини. платформи).
~~~~~~~~~~~~~~~

От надумав написати просту програму, яка ґенеруватиме потрібний лейаут на диску - конкретно, треба відформатувати SD карточку спеціальним чином. ясно, шо "проста" означала б програму-скрипт, на один раз, яка породжуватиме саме такий набір і не інакший, тобто не утиліту з партиціонування, - це на потім, на базі цього, а зараз, вона мала в один прохід згенерувати вибраний набір структур в потрібних місцях і порядку. які б на виході були або прямо кладені на картку через "сирий" режим писання прямо на диск (потім), або взагалі, руками записав би ті блоки наприклад за допомгою dd (перший варіант). (бо це для використання на платі, на якому лінукс. покишо Зображення).
Але ця скриптова природа програми, її початкової версії, виявилось, зовсім не означає її невелеличкість. От навокло цього і крутиться оця історія. Шо і побудило пописати трохи тут. Шо саме мала зробити наша програма (ff.exe - first flash, яж бачу, вам цікаво все тут Зображення)? Зробити GPT партиціонування (уявляєте - SD картка з GPT-партиціями! Зображення), і створити 4 партиції, 2 - відформатовані за типом Firmware Volume з UEFI-шної PI специфікації - це доволі примітивні контейнери для зберігання фірмварі, вони не мають папок - пласке сховище для фірмварі. Зато рівень ієрархії на 1 довший, ніж у звичайних файлових систем - бо окрім власне тому, і файлових сутностей, кожен файл розбитий на секції - і це фішка саме НЕ файлу, а FV, файл ясно шо теж може мати секції сам по собі, як от PE виконувані файли, які найінтсенсившіним чином використовуються в PI і UEFI. Дві таких партиції мені треба згенерувати - одна Boot Firmware Volume - малесенька, яка відтінятиметься (shadow) в SRAM, а він може бути мікроскопічним, наприклад на mips creator ci20 - маємо всього 14 КБ! Цей том зберігатиме найпочатковішу частину фірмварі (ту яка ініціалізувати DRAM). Другий том, такий же за типом, але більший, він відображаиметься уже в DRAM і він містить всю решту фірмварі. Крім цього, треба згенерувати спеціальну заглушку, - дані для ROM-коду шоб він зрозумів шо це треба вантажити (він вимагає цілий сектор біліберди для цього!), і одразу за цим - невиличкий "блоб", який і грузитиметься ROM-кодом і це самі початки фірмварі - SEC фаза (в термінології PI), просто безструктурний шматок коду (очевидно має бути або позиційно незалежним кодом або точно грузитися, де надумав, до речі, гарне зауваження! як нащот якшо та сторінка, куди я його цілю, виявиться "паганою"? з PEI ми б робили релокацію, а тут шо? хм, ну в ТРМ'і нічого не написано, шо ром-код робитиме якшо запитана адреса не доступна з хардварних причин), SEC - це самий початок фірмварі, приліплений до BFV як безструктурний шмат і покладений поза партиціями. Вся решта коду - уже PE-файли в наших двох FV. Через специфіку ROM-коду, цей блок (хай буде FWC - firmware cluster) треба класти на сектор 256 (LBA=256, тобто власне 257-й, бо нумерація з нуля). Цей факт теж впливає на розкрій, ми маємо пропусти 256 секторів. :/ (в сектор 0 теж можна (і в 512 і в 768), але ром-код не розуміє GPT, а GPT хоче на другому секторі свій заголовок, не сумінсі, ну а решта позицій - ще більше марнування місця, того 256.)
Отже нам треба GPT-форматована картка з спеціальним блоком поза партиціями на 257-му секторі (сектор 256), а далі 2 FV партиції, мала і більша. І ще 2 FAT партиції, одна FAT16 - це для UEFI "системної" (це термін) партиції, тут саме кладуться драйвери, OS лодарі і інші UEFI застосунки , які UEFI потім ганяє. В нормі це драйвери і ослодарі. Плюс от UEFI оболонка. Остання партиція, FAT32, - просто юзерська партиція яка займає решту (майже всю) SD-картку. Я взяв 16 GB-ну картку. Якшо цікавлять розміри партиція, то BFV - 16KB, CFV ~128KB, SYSP ~128MB, USERP ~16GB.
От оці структури, ініціалізовані, плюс маленьке наповнення, - створити одразу 3 папки в системній партиції і по приколу - покласти файл в юзерьску - це і є робота нашої "простої" програми.
Я її ще не дописав. А саме - вона ще не генерує юзерьску партицію, і ще не все пише в системну партицію, і от як ви думаєте, скільки рядків С-коду вона вже взяла? 863 рядки!!1 І це головний файл. Є ще один який читає геометрію диску, і, ясно, - заголовки з описом всіх структур і типів для GPT, FV, FAT. Дописана до краю, візьме не менше 1000 рядків. Без заголовків.
Нашо я це написав. Просто так. Я не вмію в фейсбучику так оце взять і писать, "із лічной жизні", як напр. сенсоріка. Ну не получається. Сидю тупо дивлюсь на оте "шо у вас на думці", і думаю - "та нє, ну його, це бред, потім". А оце повиправляв адові помилки в недоробленій програмі, і дозволив собі відволіктися, з чітким бажанням поросказувати за це. бо помилки повиправляв. а от коли не повипраляєш, то не хочеться нічого росказувати. Зображення
PS. Можна звичайно було скористатися чимсь готовим, хоча я сумніваюсь, шо те готове, зрозуміло б, шо я від нього хочу. Принаймні дуже навряд хтось з того готового зрозумів би FV. Але, тут таке діло, в рамках цієї роботи, треба всеодно буде написати повнофункціональні компоненти і ті, які мають справу з GPT, і модулі, які імплементують всю логіку двигуна FV (включно з можливістю робити стійкий до збоїв механізм оновлення цього сховища), і те саме для FAT. Власне НЕ в одному місці. Треба буде написати повний драйвер для FAT. Тож, варто заглиблюватися і засвоювати. Воно наче і не так складно як звучить. Але ось бачите, програма скрипт, яка типу як партиціонувальний hello world - зробила вшите наглухо і вийшла, - уже розрослася до тисячі рядків коду! Зображення
Прихований текст

Відправлено 12.09.2016 – 17:58

Уже 901 рядок. І повністю ґенерується системна партція. вибрана була FAT16 як я вже казав (того, шо кількість кластерів менша за 65536). :) Лишилась юзерська партиція. Там має бути все так само, але є певні нюасни - FSInfo наприклад, за яке я ще мало знаю, і - ЗНАЧНО більша FAT. В системній партиції, розмір FAT ми мали 256 секторів. Тут у нас буде приблизно - 4KB-байтні кластери, ~16 GB партиція, виходить кількість кластерів буде 2^34 / 2^12 = 2^22 - 4 мегакластери - приблизно 4 мілліони кластерів, а значить 16 MB FAT - бо кожен кластер має 4-байти в FAT. Це 32 кілосектори - 32768 секторів. І майже все забите нулями. Але їх ТРЕБА писати, бо вони мають такий же сенс як і ненулі. Тож, та сама робота, але "шмат" який репрезентуватиме цю частину дсиквого лейауту, буде знчано більшим.

Відправлено 12.09.2016 – 23:26

На FAT16, зарезервований реґіон складається всього з одного сектору (весь FAT том ділиться на чотири реґіони - 1) зарезервований, 2) FAT, 3) записи кореневої папки (відсутній на FAT32), і 4) область даних). Резервований реґіон в першому секторі (сектор 0 на томі, має суміш двох структур - Boot Sector і BPB, а також пусте місце.) На FAT12 і FAT16, як я вже сказав, резервований реґіон більше нічого не має. тобто далі йде FAT, а далі, - коренева папка. А от на FAT32, якась неясна каша. BS/BPB блок як завсіди - 1 сектор, далі іде 1 сектор FSInfo структури, призваної пришвидчувати певні операції (облік вільних кластерів, пошук першого вільного кластера), тобто - це розв'язок проблеми масштабованости для FAT файлової системи (на FAT32, FAT стає великою і пошук вільних кластерів страждає від проблеми масштабованости). Але для FAT32 специфікація радить використовувати 32 сектори для Резервованого реґіону. Все б нічого, та не ясно, шо там має бути. Адже BPB/BS і FSInfo складають лише 2 сектори. Також існує Backup копія "boot" сектору, вона має лежати в секторі 6 Резервованого реґіону, але специфікація каже, шо вона копіює 3 перші сектори. Шо в третьому? Нічого не сказано, крім туманної згадки, шо в третьому секторі, сигнатура aa55 теж є. Звідкіля вона там? Крім того, шо в секторах 3, 4, 5? І крім того, навіть так, Резервований реґіон мав би обмежитися 9-ма секторами (BPB/BS + FSInfo + нипанятно шо + три пропущених сектори + 3 сектори копія перших трьох). Але ні, специфікація каже, шо треба 32. Для спокійного сну, найкращої сумісности тобто.
Це залишає певну незадоволеність, від недорозуміння. але шо ми можемо тут. Заб'ємо нулями і покладемо як кажуть. Просто коли приходиться домислювати читаючи специфікаію, як треба робити, це природно напрягає трохи. А взагалі специфікація FAT добре написана, всього 34 сторінки, уявіть! Не 5000 як буває.

Вирішив таки зробити резервований реґіон на моїй FAT32 партиції, з 8-ми секторів. Бо, для мене нивиносімий факт, шо там ті сектори ні для чого, і по-друге - специфікація каже, шо мікросовтівські. системи розуміють будь яке ненульове значення в тому полі. От і добре. 8 секторів бо 0 - BS/BPB 1 - FSInfo, 2, 3, 4, 5, - нічого, 6 - копія сектора 0, 7 - копія сектора 1. Саме в шостий сектор специфікація настирно рекомендує класти бекап копію. Бо, каже, дуже багато утиліт, "хардвайред" шукати саме в шостому. Та й погодьтесь, а звідки їм знати де шукати, адже копія зроблена на випадок, шо сектор 0, став нечитабельним. А саме там написано, де копія має лежати. Тож, фіксація бекап копії зрозуміла. Як бонус, наш резервований регіон вирівняний на розмір кластера (8 секторів, 4 кілобайти).
Це ми явно відхились від рекомендацій (але не від специфікації). Ще, я зробив лише одну FAT (кількість FAT екземплярів = 1), а не дві як рекомендовано. Бо для флеш базованих носіїв, ті копії як козі коньки, тіке місце марнується. Специфікація погоджується, шо на НАНД флешах воно канєшно логічно мати 1 FAT, але попереджає, шо "в світі дуже багато паганого FAT-коду". Знову, прагнення до раціональности і ефективности, перемогло.

ну от, майже все зробив. залишилось вивести FAT в файл і все. Просто вона величезна (~16MB), і того це відкладено на самий край, бо перевіряти поля в тоталкомандері, коли файл важить 16 МБ це не так зручно, як коли він важить 64 КБ (це без FAT). Саму FAT я вже перевірив, бо з цієї кіпи, лише 17 перших записів не нульові. Хто знає будову FAT, зрозуміє, шо виділено 15 кластерів. Плюс два (перші), - зарезервовані. Справді, я в рамках цієї ініціалізації, створив 3 папки і поклав в одну з папок жепеге файл на 45КБ (звичайно з гаврилівною!11 Зображення). По кластеру на кожну папку (3), і 12 кластерів для 45 КБ-ного файлу, кластер нагадаю, у нас - 4 КБ. Виходить 15 кластерів. 17 записів в FAT.
Ну, тепер треба ще раз все перевірити, перекинути це на плату, і записати там. Це просто того, шоб пошвидше, звичайно далі треба писати одразу на картку, а не генерувати блоки образа. Їх у нас 5:
  • GPT блок. містить по порядку - Protective MBR (1 сектор), GPT заголовок (1 сектор), GPT масив партицій (32 сектори), всього 34 сектори, адреса 0 (в секторах, Lba)
  • Бекап GPT блок. містить бекап GPT масив партицій (32 сектори) і бекап GPT заголовок (1 сектор). всього 33 сектори. Адреса = розмір_диску_в_секторах - розмір_самого_блоку (33).
  • FWC. Фірмварний кластер. Містить CH - конфігураційний заголовок для ROM коду (1 сектор), SEC1 - бінарник самого початку нашої фірмварі (4 сектори), BFV - Boot Firmware Volume (32 сектори), пустий забитий VTF - Volume Top File, такий спеціальний файл тому, "останній байт якого є останнім байтом тому". Ми його використовуватимемо як сховище ЩЕ не виділеного пустого місця на томі (тобто нефраґментованого пустого місця). Далі іде CFV - Core Firmware Volume, те саме за структурою, тіке більше, але тут ініціалізований лише заголовок тому, і метадані пов'язані з VTF фалом. А також забитий верх самого файлу певним маркером, просто шоб вирівняти весь блок на розмір сектора. Сам CFV ~128КБ, тобто ~256 секторів (якшо точно, то 219), але оскільки вміст самого VTF файлу може бути довільним (пам'ятаєте - це вільне місце), сумарний розмір усієї ініціалізованої частини CFV є 1 сектор. Отже сумарний розмір FWC - 38 секторів. Адреса = 256. Чого так, бо ROM код вимагає шоб його навантаження було на якомусь з секторів (#0, #256, #512, #768). Я розказував раніше чого вибір впав на 256.
  • SYSP. блок системної партиції. Все, шо треба ініціалізувати в FAT (FAT16 тут) плюс я створив тут 3 папки. Резервований регіон 1 сектор, FAT регіон 256 секторів, регіон кореневої папки - 32 сектори, регіон даних в 3 кластери (по кластеру для папки), кластер 2КБ, 4 сектори, тож регіон даних - 12 секторів, всього 301 сектор, 150,5 КБ. Адреса - 200h (512), якраз за краєм CFV партиції.
  • USERP. блок юзерської партиції. Система - FAT32, тут як корисну забавку, я створив 2 папки і поклав 1 файл. Казав уже. Коренева папка завсіди має бути, отже її ініціалізація - це невід'ємна частина форматування. А на FAT12 і FAT16, це ще й спеціальний регіон. Створення ж інших папок і додавання файлів ішло як корисна для навчання штука. Плюс психологічний момент - побачивши папки і файл, які ти туди поклав, ти одразу 100% знатимеш, шо все вийшло. Тут резервований регіон 8 секторів, я писав за це вище. FAT гігантський ~ 16МБ, а регіон даних (непустий) - 15 кластерів, 120 секторів. Тобто тотальний розмір 128 секторів плюс розмір FAT - 724ch (29 260). Тобто 29 388 секторів, 14694 КБ. Адреса 40000h (262 144).
Давно було цікаво подивитися на флеш сховище розбите більше, ніж на 1 партицію. SSD я не маю, то дороге задоволення, а флешки, (а також SD картки, eMMC модулі), як ви й самі могли б легко переконатися, завсіди ідуть розбиті в 1 партицію. Ну це і ясно, з розміром цих носіїв, - це найлогічніший варіант. Насправді, там зробено навіть більше - так зване floppy подібне партиціонування, тобто немає навіть MBR, просто іде 1 FAT партиція. Найекономніший варіант. Але він не підходить, коли ти грузитимешся з цієї картки. Чесно кажучи це можливо теж, наприклад ROM код, який я згадував, якраз розуміє і MBR і FAT і floppy подібне форматування (а от GPT, гад, не розуміє). Тож він зміг би завантажити твою фірмварю з кореня FAT партиції, навіть без MBR, але UEFI без GPT це не те. Зображення Нам треба полігон для тестування цього діла. І воно звичайно не завадить для самого сховища. "Оверхед" мізерний, як бачите вище - увесь GPT облік з бекапом взяв 33.5 КБ. Навіть для 16 ГБ-ної картки це мало. Треба розуміти - для одноплатних компутерів (ОПК), eMMC модуль, голий NAND, - це як жорсткий диск для PC. А для таких ОПК як Raspberry PI, Pine64+, "жорстким диском" буде взагалі "ремовабле" (витягувана тобто) SD картка. Отже й лейаут ми робим там подібним. Це основне сховище для пристрою, тож розгортамо повнофункціональний лейаут для всіх потреб (потреб роботи фірмварі в нашому випадку). А взагалі фірмварю краще в NOR flash'і тримати. Якби він був - знову на ОПК чогось цю опцію опускають. Є ще Boot ділянки eMMC стандарту - тошонада, якшо вже нема нор флешу. Але це варіант, від якого ніхто й не відмовлявся. Як бачите, отой блок FWC - фірмварний кластер, він якраз би й пішов туди, в Boot ділянки. Це якшо ROM код розуміє їх, і вміє їх читати і читає. Згаданий ROM код не вміє. В новіших версіях цього сімейства SoC'ів уже вміє. SoC, який є на міпсі криейторі, його ром код, вміє це. Правда на міпсі криейторі нема eMMC модуля, там голий NAND чип, халепа, Зображення але малося на увазі, шо в принципі ром-коди знають про бутові ділянки еММЦ і це можна використовувати. В нашому ж випадку, коли ром код читає тільки юзерьску ділянку еММЦ масиву пам'яти, ми можемо класти BFV туди, а CFV разом з копією BFV в бутову ділянку. Тим паче, шо ані убут ані крепекс не використовують ці ділянки (лузери >_>). А це між іншим дві (їх завсіди має йти по 2, так каже специфікація) ділянки по 2МБ! Два метри площі! В двох екземплярах (для захисту, шоб не похєрити дані, це особливо важливо для ОПК, які не мають майже ніколи джерела постійного живлення, і можуть бути знеживлені миттєво, шо може призвести до псування даних, якшо в це момент система пише в сховище. Уявіть, якшо це писання - оновлення фірмварі! апарату лабець. а так, з двома бутовими ділянками проти цього можна захиститися, бо завсіди існуватиме одна робоча копія.) 2МБ - це більш, ніж треба. Специфікація каже, шо має бути кількість ціла до 128КБ. Є й більше, ніж 2МБ. Наприклад на еММЦ модулях, які йдуть з Одроїдами, там по 4МБ! Дві (отєчєствєнних Зображення).
Тобто ром код грузить SEC і BFV (в SRAM), і передає на SEC керування. SEC витягує з (уже відображеного в пам'ять) BFV PEI фазу, і загружає її для виконання. Просто кинути в память виконуваний файл це недостатньо, шоб його можна було ганяти - його ще треба налаштувати - завантажити так би мовити. Тобто ми маємо відображений в пам'ять (memory mapped) BFV, mmbfv і звідтіля легко розкручуємо нашу фірмварю далі. Серед модулів, які там лежать, треба мати такий, який вмітиме читати бутові длянки еММЦ і після того, як буде ініціалізована DRAM (іншим модулем, в специікації на PEI фазу, яка це робе (і лежить в BFV), і належить до PI специфікації, ці модулі називаються PEIM - пейми), він відобразить в неї (в DRAM) CFV, точно так як ром код зробив з BFV. Тільки витягуватиме він її з бутової ділянки. Тобто нам не треба вже помочі ром коду. Власне оце розбиття на BFV і CFV, саме через це, перший - початкові фази, грузиться в малий, але вже доступний SRAM, а другий - решта всієї фірмварі, - в значно більший DRAM, який ще треба ініціалізувати. А от поки ми не отримали контроль, ми не вибираємо звідки грузитися. Тож принамні сув'язь SEC+BFV має бути на сховищі загального користування (бо саме її розуміє наш архаїчний і капризний ром код, ну не наш, а тексас інструментівський). На початку, коли ми не чіпаємо еММЦ модулі1 і працюємо з SD картки - цей варіант цілком нормальний. Він нормальний і на релізових стадіях, адже як вже було зазначено, суть плати, які не мають ані еММЦ ані голого НАНДу. Тільки вставні SD картки.
----------------

1 - це для того, шоб не спортити на постійному (не витягувальному) носієві вже наявну систему - убут+крепекс тобто.
шоб не флашити щоразу картку чи чип, шоб подивитися на робочу плату. адже твоя фірмваря спочатку тіке й робитиме, шо валятиме пристрій.
але от на одній з плат в моєму паркові (cubieboard 2), я спортив вміст NAND, бо, звичайно, переплутав образ для просто SD завантаження, з образом флашера NAND'а. Я б не був собою, якби так не зробив. Ну от, тепер на нанді чортійшо, довелось закачати і зробити нарешті образ для SD
і тепер "робоча" система у мене на SD, а нанд - відкритий для експериментів. Зображення Це той випадок, коли помилка (і груба) випадково мала позитивні наслідки. Для мене чіпати еММЦ, шо вже казати за голоий нанд стало вже якимсь табу - але тут, я можу починати класти свої руки на нанд хоч завтра! Був би він добре специфікований.

Повідомлення відредагував _Ex: 14.09.2016 – 21:02


#2 Немі

    т-зло, т-дурепа, т-невдаха, т-піся, т-ононіміст. я звьозда.

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 2559 повідомлень
  • Стать:Жінка
  • Місто:Олімп

Відправлено 14.09.2016 – 21:18

Перегляд допису_Ex (14.09.2016 – 20:48) писав:

Писатиму тут (коротко Зображення)
а довго у Тебе це скільки? :lol:
  • 0

#3 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 14.09.2016 – 21:24

Перегляд дописуНемі (14.09.2016 – 21:18) писав:

а довго у Тебе це скільки? :lol:
Чогось мені лізе в голову пашлятина. :D Я й сам не знаю. Бо в мене всігда коротко, бачиш же он угорі. :lol:
  • 1

#4 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 15.09.2016 – 17:07

Ну зробив. Перша стадія пройдена - картка розкроєна правильно. "Тада!" не вийшло - ані Віндовс ані крепексовий fdisk не показувало нічого більше ніж GPT-шну Захисну MBR партицію. Це явно не все. На Beagle Bone Black, який я зву мураном (на честь свого собаки з села, двох насправді), де стоїть крепекс, я не підключав монітора, того міг бачити тіке через віддалене під'єднання (putty). А Вінда взагалі пропонувала спочатку "цей диск не форматований, зробімо це!". Потім в "керуванні дисками" стало ясно шо вона баче захисну MBR. Ну а це вже пішов подивився на крепексі вміст /dev, а також mount -l, - перелічує змонтовані фс дерева. І побачив, шо є sda1, sda2, sda3, sda4 - тобто мої чотири партиції. І, шо не дивно, змонтвана тіке одна sda4, яка має лейбл "USERP" - саме те, шо я їй присвоїв. :D І папки і файл (з Гаврилівною!11 :lol:) на місці, просто візуально не вийшло побачити в ГУЙ, але в кансолі - теж добре. Отже робе. Перші дві партиції взагалі невідомого типу, і ясно не можуть бути ніяк примонтовані розпізнавано. А от SYSP, вона ж теж FAT, чого її не примонтовано? Та того шо, вона має GUID тип (в GPT масиві партицій), визначений для "системної" партиції специфікацією UEFI. Очевидно розроби крепекса, рішили скривати системну партицію. Це правильно. Я її зміг примонтувати, побачив її лейбл "SYSP", але чогось вміст прочитати не зміг (я створив там три папки). Ну це мабуть через мою малу обізнаність з крепексом (примітка, уже зміг прочитати, правда не ясно шо змінилось, адже кроки повторив ті самі :)). Але на юзерській партиції все видно - отже все робе як треба!
Ну і для підсилення ефекту, скриншот з виведенням. Да, не вийшло поки, вав ефекту, коли б на звичайному графічному файловому оглядачі, можна було б побачити папки і файли, принаймні для юзерської партиції. Але і так, - це те саме фактично. Дуже надійне підтвердження, шо ми не тільки правильно все запрограмували, але й не помилились пишучи вручну ті блоки. доказ, шо все робе правильно.
А програма потягла на 1265 рядків. Плюс додактові файли, файли заголовків. :)
Прихований текст

Повідомлення відредагував _Ex: 15.09.2016 – 17:11

  • 0

#5 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 16.09.2016 – 01:35

Цитата

"Тада!" не вийшло - ані Віндовс ані крепексовий fdisk не показувало нічого більше ніж GPT-шну Захисну MBR партицію.
А ось і швидко знайдене пояснення, чого так.

Цитата

Answers about Windows disk support

Can Windows XP x64 read, write, and boot from GPT disks?

Windows XP x64 Edition can use GPT disks for data only.
Can the 32-bit version of Windows XP read, write, and boot from GPT disks?

No. The 32-bit version will see only the Protective MBR. The EE partition will not be mounted or otherwise exposed to application software.
Can the 32- and 64-bit versions of Windows Server 2003 read, write, and boot from GPT disks?

Starting with Windows Server 2003 Service Pack 1, all versions of Windows Server can use GPT partitioned disks for data. Booting is only supported for 64-bit editions on Itanium-based systems.
Can Windows 7, Windows Vista, and Windows Server 2008 read, write, and boot from GPT disks?

Yes, all versions can use GPT partitioned disks for data. Booting is only supported for 64-bit editions on UEFI-based systems.

Тобто, все як в моєму випадку - 32-бітна хрюшка бачить тільки захисну партицію. Шо ж, моє відчуття, шо треба витягати ноутбук, шоб подивитися результат власної творчости як хотів, підтвердилось. Або ноутбутк або товсті hdmi-кабелі. Зображення
Але це добрі новини, бо я боявся, шо наробив купу помилок і нічого крім захисної партиції - а це всього один запис в MBR з міткою 0xee, ініші системи справного не побачили. виявилося, шо все ок. Просто в Мікросовті, штучно обрізали функціональність 32-бітної версії XP. Шо ж, головне, шо воно робе.

Шо далі?
Ми це робимо в рамках проєкту зі створення фірмварі. Наша фірмваря (покишо) не вимоглива, і не вимагає переускладненого розкрою, з купою якихсь нипанятних "резервних", "рятівних" і просто незрозуміло яких партицій. Як ми бачим робе Він10, чи не дай бог - відроїд, який клепає таку бессмислєнну і биспащадну купу партицій і квазіпартицій, шо аж тошно стає.
Наша фірмваря вимагає тіке сховища для себе - це або спеціальний пристрій (NOR flash, SPI flash, eMMC boot площі), або сховища в ділянці загального користування. В будь якому разі, це наш FWC - фірмварний кластер. Або FSD Firmware Storage Device - фізичний чи логічний. У випадках, як з нашим першим на мурані (це ім'я Beagle Bone Black плати, розумієте, чого спеціально дане ім'я - це простіше, ніж довге ім'я плати з довгими поясненнями?), так от, на мурані, у нас "складний випадок". А саме, ром-код НЕ розуміє читати бутові ділянки еММЦ, хоча плата й має еММЦ модуль і дві бутові ділянки по 2МБ, як уже було сказано. Тобто ми маємо де покласти нашу ФВ (фірмваря), шоб і не попсував її користувач, і шоб місце вона не їла його, користувача, на ділянках загального користування, але через обмеження ром-коду, ми не можемо цілком покласти нашу ФВ туди. (шо таке взагалі цей "ром-код"? це код вшитий в ROM на чипі який починає виконуватися на reset'і і який власне завантажує наступну порцію коду для подальшго вантаження і керування пристрою - фірмварю. а та грузить ОС. Тобто, це теж фірмваря (найпервіша її частина), від виробника SoC'а, незмінно вшита в чип. Перші фази на PI називаються SEC, оскільки на ром-код ми не впливаємо, ми розділили SEC на дві порції, ром-код обізвали SEC0, а свій початок - SEC1.)
Шо в такому випадку робити? На перших етапах, ми просто кластимемо наш FSD, в загальне сховище цілком, а коли наша ФВ стоятиме впевненіше на ногах, перенесемо більшу її частину в бутові ділянки еММЦ, лишивши тільки ту частину FWC, яка безпосередньо вантажитиметься ром-кодом, і яка матиме модулі, які вмітимуть читати еММЦ бутові ділянки. А читати їх має бути не складніше, ніж решту, просто шлеться спеціальна команда, чи виконується певна послідовність команд і дій, яка каже еММЦ контроллеру переключитися на читання бутових ділянок, і навпаки. Адресуються вони цілком незалежно від загальної площі. Тобто тоді, логічно, наше ФВ сховище, розіб'ється на два кластери: перший - SEC+BFV на загальній площі (BFV як перша GPT-партиція, SEC - безпосередньо перед ним, але поза партиційним обліком), і другий - CFV (і копія BFV) на окремому пристрої (бутові ділянки еММЦ модуля).
Але на перших етапах кластер один і він на SD картці. Як було описано вище. Так само - на платах без постійного сховища (другий тип плат - Raspbery Pi, Pine64+, в мене є друга, вона має armv8, 64-бітний арм).
Отже FSD - це сховище для самої ФВ, ця назва акцентує саме на пристрої, а FWC - на будові самого сховища (SEC+BFV+CFV). Який розмір для ФВ сховища ми встановили? Покишо ми вимагаємо 128КБ. Дійсно, в нашому розкроєві, який ми успішно прописали на картку, видно - увесь FWC лежить між секторами #256 і #512 (не включно). Тобто 128 КБ (сектор тут 512 байтів). Наші апетити доволі скромні, і наприклад, сучасні еММЦ модулі пропонують бутові ділянки в кілька мегабайтів розміром. Це наша мета - бути ефективними. І в плані споживання простору.
Далі, ми маємо вимогу мати на системі принаймні одну системну партицію. Цього вимагає UEFI специфікація. І ми встановили її розмір в 128МБ. Потім виявилось, шо наприклад МС, вимагає шоб системна партиція була як мінімум 128МБ. Тобто, ми вибрали дуже економно. Цікаво, шо МС вимагає шоб систмена партиція була FAT32, хоча UEFI не вимагає шоб саме FAT32, просто будь який FAT. І самі майкрософтівські драйвери, ясно, як ніхто інший запиляні обслуговувати усі типи FAT з усіма "історично обумовленими" фокусами і збоченнями. Між іншим як наслідок цієї дивної вимоги, на сучасних наворочених дисках з секторами в 4КБ, мінімальний розмір системної партиції стає 256МБ. Гаразд, це я відхилився, це вимоги МС до ОЕМ, вони не порушують UEFI, це я до слова, просто дивно нашо лише FAT32? Звичайно, хоч UEFI й вимагає будь яку FAT, під "будь якою" практично мається на увазі. або FAT16 або FAT32, бо єдиний сенс морочити голову з півторабайтними FAT-записами FAT12 - це мати шось дуже древнє і маленьке як флопі диски. Ви повинні зрозуміти, шо через довгу історію цієї ФС, вибір ЯКИЙ саме варіант робити - залежить фактично від кількости кластерів, які утворять ділянку даних тому. Наприклад, якшо в тебе виходить кількість кластерів десь коло 65535, але менше, це FAT16. І так само, якшо кластерів < 4096 (точне число трохи менше), то це лише FAT12. Тобто існує таке дивне правило, яке сформувалось від довжелезного досвіду використання цієї ФС - не існує FAT16 томів, в яких менше, ніж 4096 кластерів, не існує FAT32 в яких менше, ніж 65536 кластерів. Знову, точні числові значення цих межових значень - трохи менше, ніж наведені цілі степені 2, - довга спадщина. Саме того, якшо у вас диск з 4КБ секторами, через вимогу лише FAT32 (а значить кількість кластерів > 65536), ви матимете системну партицію не меншу, ніж 256МБ (2^12 * 2^16 = 2^28 = 256 МБ). Але з обсягами марнування ресурсів сучасним ПЗ різного кшталту, це просто верх економности.
Вимога ж нашої ФВ - мати системну партицію. Та й усе. А вибрали ми її розмір в 128МБ. Це те, шо визначає наша ФВ. Подальше розбивання дисків - на розсуд користувача. "Основний" диск на системі має мати GPT партиціонування, і встановлення нашої ФВ зробить там 1 системну партицію в 128МБ.
Шо кластиметься в системну партицію? Взагалі, все, шо пов'язано з завантаженням машини. Драйвери UEFI, можливо також. Але перш за все - "застосунки". Завантажувачі ОС, діагностичні утиліти, різні дані для цих програм. Все, шо треба, шоб вантажити систему. Ось також оболонка UEFI, для користувацького рукоблуддя на рівні ФВ. Але, в принципі, треті сторони, можуть класти туди навіть ядра і модулі своїх ОС (хоча б могли класти їх деінде, і їхній ОС лодарь міг би звідти те ядро грузити). Наприклад, якшо подивитися на "аналог" системної партиції на системі, яка зараз превалює на ОПК, - тобто сув'язь убут+лінукс, це не UEFI однозначно, але на тамошньому аналогові системної партиції, в "boot" партиції, вони кладуть не лише убут, але й гігантське, роздуте ядро самого лінуксу, і це ж воно ще стиснуте! А також всяку іншу свою фігню, типу initrd чи шось таке - теж гігантське, та ще і в кількох екземплярах. Отже, "вендори" знайдуть чим захламити системну партицію. Саме міркування над цими фактами, побудили нас виділити 128МБ, тоді як для самої ФВ ми виділили в 1024 разів менше! І, виявилось, наші міркування цілком співпали з вважанням інших учасників. Наприклад вимоги МС вище. Або та ж згадана "boot" партиція для убута і крепекса. Вона (теж FAT16 на мурані до речі) десь коло 100 MB.
Ми зробили системну партицію FAT16.
А шо ж далі в контексті цієї нашої утиліти? Та й в контексті самої ФВ? Нам треба організувати зручний процес оновлення на нашому системному диску. Шоб не доводилося шоразу переписувати все з початку, включно з GPT структурами, які від модифікування вмісту партицій не змінюються, і могли б собі спокійно лежати далі. Ми маємо зробити раціональний метод закидання в це сховище наших модулів ФВ і всієї решти даних, які можуть знадобитися (включно з картинками з гаврилівною!11 Зображення).
Шо ми маємо за "гарячі" місця модифікацій і який їхній рівень гранулярности так би мовити?
Перше - це SEC блок - контейнер в 4 сектори, який містить перший код ФВ. Він поза обліком, і його оновлення - це очевидно перезаписування цих 4-х секторів. Сектор, шо йде перед ним, CH (Configuration Header), він не має змінюватися - там формат для ром-коду, шойно ми переконалися, шо ром-код зрозумів - це воно, ми нічого там не мінятимемо. Там нічого конфігурувати, насправді, попри назву. А використовуватимемо ми вже перевірений вміст. Тож має робити.
Тобто на рівні SEC - просто перезаписуємо 4 сектори на оновленні. Можна додати в самий край цього блоку хвостову сигнатуру, шоб мати додаткову впевненість, шо ми пишемо куди треба. Це ж писання на диск - помилитися НЕ можна!
Далі BFV і CFV, з однаковою структурою але відмінні кількісно. Їхнє оновлення, тобто додавання туди, видалення звідти тощо, можна було б зробити таким же простим як і з SEC шматком - перетерти все. Але це не надто раціонально. Особливо для майже 128 КБ-ного CFV. Того, нам треба навчитися оновлювати пофайлово, посекційно. Придумати командний інтерфейс для цього і втілити його. Там особливо не розгуляєшся - пласка череда з файлів. Але от, знаючи, шо на розробницьких етапах, багато файлів ростимуть в об'ємі, ми б могли зробити певну оптимізацію в кладенні самих фалів. В цьому форматі є така концепція як padding файли, які використовуються для вирівнювання наступної сутности на потрібне число. Але ми б могли використовувати це і для алокаційної оптимізації. Шоб на оновленні не перетирати весь том, ми б могли, якшо нова версія файла більша але padding файл за ним дозволяє це, нам би довелось перетерти тільки цей фал і не чіпати решту. Тобто ми б відняли простір від padding файлу, і здивгати все шо йде далі, було б не треба. От на додачу до командного інтерфейсу, нам варто і над цим подумати. Отже, оскільки наші томи ніким не розпізнавані, шоб нам писати в них "пофайлово" і "посекційно", тобто на рівні абстракцій і семантики цієї ФС, а не грубо, на сирих секторах перетираючи усе підряд, щоразу безжалісно затирючи увесь том, нам власне треба імплементувати юзерспейсний драйвер цієї ФС. Це наступне завдання для цієї утиліти. Ти задаєш команду, шо от цей файл в цьому томі треба поміняти, і твоя утиліта шукає, рахує, перевіряє, згідно з правилами FV, чи це можливо. і якшо так, - пише його. Як вона пише? Та як і ядерний драйвер, - перераховує зрозумілі їй сутності в зрозумілі нижчому рівню і запитує операцію в нижчого рівня. "Нижчим рівнем" тут буде "сирий" режим писання на диск. Коли адресується фізичний пристрій в обхід ФС дрів.
В підсумку, це не має бути надто складно - SEC оновлення це просто перетирання фіксованої платформно специфічної позиції, там основне - забезпечити правильність адресації. BFV на мурані - це початково 2-3 файли. в 16КБ-ах. Тож, знову має бути досить просто. CFV більший і варіанти складніші, але логіка та сама. Відточивши її на простішому BFV, потім уже буде простіше на більшому CFV. Але до CFV ще далеко як до Києва рачки. Це все балакається за сховище. А от чим його наповнювати - це і є сама ФВ. І тут покишо, ми на SEC. Тобто на самому початку. З т.з сховища, як ми вияснили, тут все доволі просто. Так шо, далі мабуть побалакаєм за шо ж власне ця SEC робе.

Повідомлення відредагував _Ex: 16.09.2016 – 01:59

  • 0

#6 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 16.09.2016 – 02:07

А в перервах між балаканиною, робитимем. :D
  • 0

#7 серпиня

    Старійшина

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1084 повідомлень
  • Стать:Жінка

Відправлено 16.09.2016 – 14:28

Валєра ушол в сєбя... Валєра, вернись в сім'ю!
  • 0

#8 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 16.09.2016 – 21:39

Перегляд дописусерпиня (16.09.2016 – 14:28) писав:

Валєра ушол в сєбя... Валєра, вернись в сім'ю!
Та нікуди він не ушол, це цікаво насправді. між іншим коли займаєшся цим, не заморюєшся, встаєш з легкою такою мінівтомою, а коли відволікаєшся і робиш шось інше - заморюєшся. он вчора писав отой допис вночі вже, доволі довго писав. Шия заболіла, очі вивалюються. От так от.
  • 0

#9 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 17.09.2016 – 01:03

Шо SEC фаза має робити? З одного боку, вона має відповідати за приведення процесора і системної логіки в відповідний
стан. Наприклад налаштування PLL, тобто тактових сигналів (частот), ром-код шось вже сконфігурував, а шось ні, нам треба вияснити, шо нам можна не чіпати, а шо треба конфігурувати, а шо можливо треба переконфігурувати. Так само стоїть питання стану MMU, кешів першого рівня, там ще branch predictor, вгадувач галуження, так би мовити - конфігурувати його чи ні? Наприклад мати кеш, L1 кеш, ввімкненим, було б не пагано. Але, тоді мабуть треба вмикати MMU. Постає питання: як саме його конфігурувати на цьому етапі? От ФВ на всьому своєму протягу каже, шо MMU має бути "дозволене", але має підтримуватися identity mapping, тобто тривіальна відподність між віртуальними просторами і фізичним простором пам'яти, тобто функція трансляції - y = x. Ще треба вияснити, чи має така форма відповідности якісь особливі підходи з боку архітектури, адже ясно, шо тут відпадає (могло б відпасти) багато мороки - не треба насправді робити трансляцію, фізична адреса щоразу така сама як віртуальна. Але невідомо (мені ще), чи "знає" про це ARM архітектура, чи для неї це нічим не відрізняється від інших варіантів, і всеодно треба будувати таблиці сторінок, вмикати TLB, і налаштовувати всі оті безкінечні регістри. Хотілось би, - шоб перший варіант, це ж логічно. Якшо MMU розрізняє "відображення ідентичности", роботи буде менше.
Далі, іде така архітектурна вимога з боку процесора, як обробка винятків. В принципі, ром-код уже надав доволі тривіальні обробники, які зрештою зводяться до "dead loop"-ів, які просто крутять процесор поки Watchdog таймер, таймер сторожового собаки, Зображення не ресетне (reset) машину. На перші рази, може цього й вистачить, але надалі, треба перебирати на себе роль в обробці винятків і робити це значно наповненіше змістом, ніж чекати на сторожового собаку. Не всі винятки ми можемо обробляти, принаймні reset і monitor виняток (перехід в режим монітора на arm'і зроблений як виняток,
SMC інструкція ґенерує особливий виняток), ми не можемо обробляти, бо конкретно на цьому SoC, ми не маємо доступу до Secure World. (Це такий привілейований режим з доступом до привілєйованих ресурсів, все дуже наплутано, як і решта в arm'і). Шоб ви мали уявлення, принцип роботи розділення Secure World/Non-Secure World такий - "чорт ногу зломе".©
На деяких інших армівських процесорах, наче як маємо такий доступ. Наприклад на Allwinner'івських. В мене їх аж два - a20 і a64. Але тут не маємо. Нічого, розберемося, a20, в мене іде другим в черзі на arm'і.
Але такі винятки, як "невизначена інструкція" (undefined instruction), fiq'и irq'и аборти різноманітні, ми маємо обробляти. І, ясно, шо постачити обробники треба як найраніше. Отже це ще одне з завдань SEC - надати обробники армівських винятків.

Далі ідуть вимоги до SEC з боку архітектури ФВ.
Тут треба одразу зауважити, шо, як навіть сама назва фази натякає, вона має великий бізнес в обслуговуванні Security - безпеки, загальної безпеки пристрою і його вантажувального аспекту - Secure Boot наприклад в UEFI. Все має починатися
з довіреного "кореня". От SEC має бути ним, і ініціалізувати відповідні заходи. Автентифікація подальших фаз, ланцюжок довіри, сертифікати, підписи, хеші, і проча криптографія. Це має дуже тісний зв'язок з отим Secure Extension, згаданим вище, власне це останнє, є архітектурним розширенням arm'а для втілення різномантіних вимог безпеки і цього також - верифікованого завантаження пристрою тобто. А ще DRM всякий і тому подібне, все, де крутяться гроші. Так от, на перших етапах розробки у нас цього немає. Грошей немає. Зображення Зображення Немає безпеки. Зображення І ми опускаємо її з розгляду поки шо.
Але воно буде додано. Просто це дуже важко - одразу вгрузати в усі оті сертифікати в той час, коли нам треба
вчити складнючий interconnect на SoC'у, PRCM - power, reset, clock management і подібну дуже важку і неясну бяку. Того забудьмо поки за безпеку, "let's get wasted, yeah!" Зображення
А крім безпеки, SEC фаза має такі вимоги ФВ задовільняти, - саме, вона має завантажити Pei фазу, і підготувати для тієї фази певну інформацію, також мабуть побудувати для неї певні структури і частково вставити туди дещо. Інціалізувати середовище для Pei - налаштувати стек наприклад. Завантажуючи PEI, вона може зіткнутися з проблемою недоступности тієї адреси в пам'яті, на яку PEI прив'язана (злінкована) - тоді треба робити релокацію - проходити по коду і перетирати поля з адресами. Це не так важко. Імовірність шо таке знадобиться мала, я думаю, але ігнорувати це не можна. Ром-код я так розумію, ігнорує це. Але нам треба рівнятися на "як правильно" а не навпаки.

От уявіть такий сценарій, який змусив мене не нехтувати релокацією в SEC. Образ Pei.exe зав'язаний вантажитися на певну адресу. Ми її знаємо, бо вона SoC-специфічна, на кожному SoC'у є трохи SRAM'у, якому присвоєно свій діапазон в фізичній пам'яті, і ром-код, там виконується, і виділяє частину (мабуть навіть більшу) для подальших фаз. Ми знаємо цей діапазон, по карті пам'яти SoC'а, і ми прописуємо ці адреси в наших взаємодіях з ром-кодом і внутрі наших модулів, де треба. Так, ми задаватимемо якусь адресу (з можливих) для самої SEC, згадайте, я казав про це вище, - ми кажемо ром-коду, куди вантажити SEC, так же ми знатимемо куди має попасти Pei.exe. Того, ми лінкуємо її з певною адресою. А шо, якшо ця адреса виявиться "битою"? Я чесно не знаю, наскільки цей сценарій імовірний, але точно він НЕ нереальний. Ясно, можна сказати, шо якшо в SRAM'і полетіли сторінки - це катастрофічна ситуація, пристрій негожий. Але, можливо там все не аж так пагано, і решта сторінок гожі? Шо відбудеться, якшо ми знехтуємо релокацією і (власною) обробкою винятків? На ресеті, ром-код почне вантажити нашу фірмварю, вона дійде до вантаження Pei.exe, і намагатиметься писати в ту пам'ять, куди ми прив'язали образ. Через "битість" цієї ділянки масиву пам'яти, це викличе який небудь Data Abort і процесор почне виконувати обробник цього аборту. Оскільки в ром-коді обробники - це просто чекання на Wacthdog таймер, провисівши в тому "dead loop"-і, процесор зазнає ресет, від Watchfdog'а, і ... знову почне грузити мою фрімварю. Яка знову викликатиме data abort. Це саме така поведінка, яку показував мій старий компутер, на ім'я "друг", Зображення ось він стоїть біля мене на столі - не хоче він запускатися, тіке отак перевантажується безкінечно. Шось полетіло (апаратно), а шо - хз, але 99% все гоже й робоче... От бачите, який неприємний сценарій. Це одразу каже нам, шо і релокацію треба вставляти в SEC, і свої продвинутіші обробники теж, і якнайшвидше. Продовжуючи, якшо наприклад, наступна сторінка гожа, ми в обробнику аборту виявимо, шо спричинило його, вернемось в SEC'овий PEI завантажувач, зробимо крок на сторінку вперед і намагатимемось туди писати. Там - гожа сторінка, все минулося, але тут, ми вже не можемо просто так вантажити образ - треба робити базову релокацію - виправляти адреси, вони тепер "трохи" не відповідають реальності. Але це вже просто. Ще треба повідомити, шо файл було б непагано перелінкувати в самому BFV (шоб не побадати в аборт наступного разу), тобто зробити певне оновлення. Пізніші фази, які вміють писати на FSD - сховище, де той образ лежить, -
вони можуть "пропатчити" його, і наступного разу ми оминемо взагалі "пагану" сторінку. (Оновлення фірмварі це ахтунгзвичайно, але шо поробиш.) На кінець цього відступу, варто ще раз повторити: мабуть таки сторінки в SRAM'і мають завсіди бути гожими, і подія описана вище - ультрарідкісна.

Ідемо далі. PEI має таку концепцію, як база даних PPI - інтерфейсів між PEIM'ами - модулями PEI. Хоча це наче лише для PEI фази, але специфікація каже, шо SEC, тут теж може шось таке інсталювати, шо можливо потім знадобиться. Можливо навіть обробники винятків, згадані вище, таким чином можна експонувати, хоча це просто приклад, вони напевно будуть не потрібні дальшим фазам (процесор на винятку і так стрибне на них, бо ми ж сконфігуруємо відповідні регістри в ньому, передавши адресу таблиці наших векторів). Але от стан здоров'я процесора можливо можна передати за допомогою цих SEC PPI. Як він запустився. Зрештою шось мені здається, шо починати будувати ту БД PPI, доведеться в SEC фазі.

Тобто отак - налаштувати мінімум необхідного в процесорі і системно (частоти, вольтажі можливо теж це отой PRCM). Надати обробники винятків, і прописати їх. І починати завантажувати PEI. Пам'ятаємо, ром-код завантажив нашу SEC і BFV в пам'ять. PEI лежить саме в BFV. Але весь том лежить як кеш. Це наш кеш. Завдання SEC - знайти там файл, який містить так звану PEI Foundation, у нас це буде файл Pei.exe, і завантажити його вже для виконання. Ми вже раніше казали, і наводили про релокацію - завантажити просто в пам'ять, і завантажити на виконання - це не одне й те саме. В тому файлі (Pei.exe), точкою входу має бути спеціально домовлена функція, яка приймає hand off інформацію від SEC як свої аргументи. Там іде опис діапазонів пам'яти, які доступні взагалі, а також, - які доступні саме для Pei, також - де лежить стек. Який SEC уже налаштувала. Плюс виділена можливість для якихсь додаткових речей, які можуть бути передані. Як оті PPI від SEC про які я міркував вище. І про які рішає імплементатор. І, далі, SEC має передати керування на цю функцію. Все, SEC зробила свою роботу. Далі працюватиме PEI і її модулі.

Побалакаємо тепер за фізичний лейаут SEC фази. Ми вже казали, шо це не може бути якось форматований виконуваний файл, ром-код не розуміє цього, все, шо він розуміє - це голий бінарник і невеличкий заголовок на самому переді. В якому два 4-байтні поля - адреса, куди треба вантажити те, шо йде за заголовком, і розмір який треба вантажити.
Ну а ми виділили для SEC 4 сектори, тобто 2048 байтів. На платформах, де сектор є в 4КБ, ми б виділили 1 сектор. Зображення
Розкрій насправді дуже простий, а як інакше в такому ранньому і обмеженому середовищі? Складатиметься SEC з двох
секцій - секції коду і секції даних. Розділення іде так: в 16-ковій СЧ, 2048 - це 800h, ділимо так - 600h на код, і 200h
на дані. Тобто три сектори коду, а далі сектор на дані. Треба чаклувати з директивами асемблера так, шоб задати потрібний розкрій уже на стадії асемблювання. І це, я вірю, можливо. ARM має інструкції всі в 4-байти. Тож скількома інструкціями ми себе обмежили? 600h / 4 = 180h = 384 інструкції (насправді 382, бо два перших слова - це згаданий заголовок для ром-коду). І 128 4-байтних слів для даних. Має вистачити. А якшо ні, доведеться переформатовувати. Оце й увесь розкрій. Оновлення цієї частини ФВ, як ми вже згадували в попередньому дописі, теж - просте як двері - ми просто перетиратимемо старі 2КБ новими в тому самому місці, роблячи при цьому перевірки, шо ми перетираємо те, шо треба.

А яка ж мова імплементування цього всього? Часто ми бачимо в аналогічних проєктах, як швиденько після мікроскопічного
стартового шматка, який інакше як на асемблері не напишеш, розроби скоршіе тікають в нетрі C. Зображення Ми так не робитимем.
І SEC і PEI планується писатися на асемблері. Ми любимо C, але ми також любимо асемблер. На C будуть написані дальші, складніші алгоритмічно (і гастроентерологічно) фази.
Попри спартанську непривітність гнусного асемблеру, яким ми змушені користуватись, бо всі інші якось намутили так, шо їхнє використання не видається можливим. І я того не буду лаяти гнусний асемблер, це було б невдячно якось, хоч він і йде з FSF. Та й є в ньому гарні речі. Тобто, шоб ви розуміли, я не кажу, шо асемблер від гну паганий, просто я б з радістю користувався майкрософтівським. Але де він? На arm і шоб писати довільний код, не прив'язаний до ОС чи до якихсь бібілотек. Гнусний асм має таку не дуже дружню рису, шо він запиляний на виведення від гнусних же компіляторів. І аж ніяк не людей асемблерописців. Того нема ніяких ніштяків в ньому, ніяких няшних механізмів описувати структури наприклад.
Хоча на асемблері б, це мало бути так же просто як і на C. Якби було бажання від творців конктретного асемблера (як програми). Але з цим можна якось боротися і пристосовуватися. Але цей Linaro підготований набір дає тобі можливість писати навіть на голе залізо. І безкоштовно дає. Компілятор від Арма, самої кантори, стоє грошей і його асемблер має доволі чудний синтаксис. Крім того, в мене ще є mips, і там теж, те шо є - це знову гнусний асмеблер. Тож, варто звикати саме до його збочень. Шо шо, але він доступний. І це його перевага. На відміну від інших. Майкрософт явно робе помилку не випускаючи вільно компілятори на arm. Так же вільно як на x86. Шоб можна було робити все. Може він і доступний в WDK 8, я так толком не зрозумів, але, шо мені з того, - моя розробницька машина - це Windows XP і Pentium 4. Десятка туди не ставиться. А на XP не ставиться WDK 8...
Через дивну вимогу гнусного асма надавати обов'язково розширення .S тим асемблерним файлам, які мають підлягати обробці препроцесором (а це зручно, це знайомий C-шний препроцесор), замість звичного для (Wintel'івських адептів) і кулхацкерівського .asm, наш файл, який імплементує SEC фазу, зватиметься SEC.S. Зображення Як співає Гаврилівна - "little things that make life great". Зображення

Це був крупнозернистий огляд SEC фази, шо вона має робити. Далі, описуватимемо предметніше.
  • 0

#10 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 18.09.2016 – 16:27

Спішу похвалитися певними локальними успіхами в справі побудови першого коду нашої ФВ. SEC фази тобто. Я навчився ґенерувати цей файл з потрібним розкроєм. Тут варто зауважити - я працюю з gcc набором, від linaro. я вже казав за це. отже інструменти і їхні правила - звідти. Виявилось, шо така концепція як скрипти збиральника (лінкера), ld, дуже підходять для мого завдання. Треба сказати, шо хоча gcc і уміє ґенерувати PE, нажаль його підтримку не вставили в linaro набір, а конпілірувати свій власний gcc я не хочу. Тож доводиться все робити через жопу ELF. Зображення Шо ж, це терпимо, нам всеодно потрібно його знати. Але саме в цьому випадку, випадку SEC, - незалежно від підтримуваного формату (вихідного виконуваного файла), нам би всеодно довелося робити з нього голий бінарник, тобто зрізати всі метадані, лишаючи лише валідну послідовність наших секцій. Виявилося, шо є така можливість це зробити - утиліта objcopy вміє вирізати з ельфів все, лишаючи лише бінарні дані. Тож, оце й є локальний успіх - ми створили скрипт лінкера, закинули в наш SEC.S трохи армівського коду, який "шось там налаштовує", і прогнали це. На виході отримали, те, шо нам треба - чітко 2КБ-ний блок, з хвоствою сигнатурою для верифікації перезапису, про шо я казав ранше. Я її туди поклав теж засобами лінкувального скрипту. Зображення
Для іллюстрації, я наведу код скрипту лінкувальника, SEC.ld, і код нашого SEC.S але не забувайте, в другому - власне кажучи просто правильний синтактично код, але без всякого змісту по суті. Хоча, це взято з стартової послідовности з прикладу від самого ARM'а Ltd. Просто це невеличкий шматочок, який майже нічого не робить. Ну по коментарях видно, шо він намагається робити - доволі серйозні речі. Але це просто ілюстративне наповнення поки шо.
Прихований текст

  • 0

#11 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 22.09.2016 – 22:26

Трохи про MMU. UEFI каже, шо воно хоче, шоб MMU було ввімкнене (enabled), але хоче, шоб було identity mapping, тобто коли "фізична" і "віртуальна" адреси ідентичні завсіди. Як я вже казав, одразу постає питання - цей режим явно по своїй суті не потребує трансляції як такої, а значить - дуже дорогого обходу таблиць сторінок. Але це по суті, а як насправді зроблено в тій чи іншій архітектурі? Ясно, шо якшо MMU вимкнене, то це теж "identity" відображення, але це суперечить букві специфікації, і інтуїція каже, шо ця розбіжність явно не просто тривіальна і матиме відчутні наслідки, якшо нею знехтувати.
Отже після вияснень, ми вияснили. ARM має так зване "flat mapping" фізична і віртуальні адреси чисельно збігаються. Але. Це досягається ... вимкненням MMU. Далі, підтверджується, шо ця розбіжність між UEFI'шним "ідентичним відображенням" і ARM'івським "пласким відображенням" не зовсім тривіальна і не зовсім лежить лише в питанні чи MMU ввімкнене чи вимкнене. Будь так, то це було не так і страшно. Адже "вимкнене" теж буває по-різному, можна було б знехтувати UEFI'шною вимогою мати MMU "ввімкненим". Це б звелося до невирівняности понять - армівське "вимкнене" MMU всеодно робить тривіальне відображення 1:1 і приписує сторінкам певні атрибути, тобто можна сказати за фактом - забезпечує уефішне ідентичне відображення. Але.
Нетривіальність розходження полягає в можливості кешувати. А саме, лише кеш інструкцій буде працювати за вимкненого MMU, а кеш даних де факто - не працюватиме, і всі доступи до даних трактуватимуться як Strongly oredered data accesses - власне некешовоні доступи. Отже, вимикаючи MMU і роблячи легкий спосіб досягти ідентичне відображення, коли залізо розуміє, шо власне адреси збігаються і не треба нічого транслювати і все от TLB і обхід таблиць - багаторівневий при тому - не потрібні - це все на ARM'і є, але ціною жертвування такою фігнею як можливість використовувати кеш даних.
От і вибирай. Звичайно треба вмикати. Хто ж не хоче використовувати кеш процесора! Ідеться про кеш першого рівня. Всеодно це було б треба робити, пізніше - на рівні ОС лодаря, який би підготовував це для ОС. Просто якось воно тупо налаштовувати таку складну фігню для того шоб грубо кажучи прописати шо X = X. Зображення От уявіть, ви маєте доступ до адреси 4030cafe. І це перший доступ туди. В TLB, а це кеш записів таблиць сторінок, нічого за цю трансляцію немає, виникає "промах" TLB, і процесор починає дуже дорогу операцію обходу таблиць сторінок. Якшо ви не поклали в таблицю потрібний запис, виникне page fault (або взагалі data abort), але якшо поклали, нарешті процесор полізе туди шоб подивитися, яка фізична адреса відповідає цій віртуальній адресі. Плюс ще атрибути перевіряються (дуже багато всяких атрибутів - доступ, тип пам'яти, чи виконувана і тп), окрім вже згаданого "атрибуту" наявности сторінки взагалі (коли запис в таблиці взагалі не нульовий), є ще soft page fault ситуація - коли запис відображення існує, але сторінка помічена як не присутня. Це і є механізм для сучасного paging'у! Коли сторінка або реально є в пам'яті, або лежить в своєму "задньому" сховищі. Те, шо Віндовс називає пейджингом і підкачкою. Це і справді підкачка. (інтел пейджингом називає той факт, шо його процесор нарешті відкараскався від тих ацьких сегментів і використовує сторінкову організацію пам'яти Зображення). Дурнуватий лінукс називає підкачку свопом. Але це не своп вже років 40. Своп був дійсно, коли ВСЯ пам'ять процесу викидалась і мінялась на пам'ять іншого. То був своп, тобто обмін, а це підкачка, сторінкування - посторінкове керування пам'яттю, а не попроцесне. Одні сторінки викидаються на своє заднє відображення (тобто "брудні" сторінки справді пишуться або у власний файл, який служить для відображення в пам'ять, або на файл підкачки, який лове сторінки, які не мають своїх файлів відображення, наприклад динамічна пам'ять процесу - вона не має свого файлу, сторінки ідуть в файл підкачки. "Чисті" сторінки, вміст яких синхронізований з вмістом файлу, тобто або вже синхронізовані сторінки, або сторінки тільки на читання (як напр виконувані секції образів) просто викидаються з обліку в пам'яті). М'який пейдж фолт, це коли сторінка начебто викинута з пам'яти (вона викинута з робочго набору процесу), але насправді знаходиться в stand-by стані - є кандидатом на справжнє викидання, і її вміст валідний. Якшо процес знову хоче цю сторінку, все, шо ОС треба зробити, - це помітити в записі (таблиці сторінок), шо сторінка гожа - операція дуже швидка, того - м'який пейдж фолт. Якшо ж процес так і не звернеться до цієї сторінки, з stand-by стану, вона буде переміщена до вільного стану, і її вімст буде занулений (в більшості випадків, через питання безпеки). Далі її віддадуть комусь іншому, може навіть тому самому процесові (якшо це наприклад сучасні бравзери, то імовірність цього висока, адже вони їдять сотнями мегабайтів пам'ять, просто ні на шо.) Так от, повертаючись до трансляції, і от, процесор в нашому випадку, випадку ідентичнісного відображення, все оце шукання робе, шоб подивитися, яка ж фізична адреса відповідає нашій "віртуальній" (дуже невдала назва) адресі 4030cafe? І шо він знаходить? Так! він знаходить в таблиці запис, який каже, шо віртуальній адресі 4030cafe відповідає фізична адреса 4030cafe! Круто чи не так? І так для будь якої адреси. Але тільки так можна використовувати D-кеш. Сам же механізм - надзвичайно потужний і потрібний. Просто це стає видно, коли справді існує купа віртуальних просторів (адресні простори процесів), які відображаються на один фізичний. А для ідентичнісного відображення, цього можна було б і не використовувати. Але архітектура має свої капризи.
Отже вмикати. Коли? Покишо вирішено робити це в PEI фазі. Такий собі компроміс - до ініціалізації DRAM, працюватимемо без D-кешу, а після, вмикатимемо MMU, і налаштовуватимемо таблиці сторінок і всю решту.
I-кеш (кеш інструкцій) і вгадувач галуження (branch predictor), використовуватимемо одразу.
  • 0

#12 Kassandra

    Т-Сонятко

  • СуперМодератори
  • PipPipPipPipPipPipPipPipPipPip
  • 5187 повідомлень
  • Стать:Жінка
  • Місто:сплячих левів

Відправлено 23.09.2016 – 11:17

флейм почистила


  • 0

#13 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 25.09.2016 – 02:42

Трохи напишу про середовище і умови, в яких будуть виконуватися наші перші фази, тобто SEC і PEI.
PEI до речі розшифровується як Pre EFI Initialization, тобто - ще одна "перед"-фаза ініціалізації. Дуже багато розкочегарювань.)
:^)

На мурані, тобто на машині з SoC Sitara AM3358 від TI, ми маємо SRAM для цього. В більшості випадків на ОПК - це срам, але от на mips creator ci20, ділянку, відповідний манул, обзиває tcsm, і її там дуже мало, як я вже згадував, всього 16 КБ, з яких нам доступно 14 КБ, tcsm, я думаю, це tightly coupled static memory або tightly coupled system memory, тобто - те саме. А от на Ітаніумах, пишуть, для цих цілей кеш процесора конфіґурується як невеличкий масив пам'яти. А на ОПК це срам. І на am3358 його трохи менше 128КБ. Це набагто більше, ніж на криейторі (машину якого я назвав йод).
Отже ми починаємо за вимкненого MMU, також ром-код залишає вимкненими І-кеш, Д-кеш і вгадувач галужень (ВГ). Ми повторно для впевнености, вимкнемо MMU на самому початку, і вимкнемо Д-кеш. А І-кеш і ВГ спробуємо ввімкнути. Коли ж відповідний модуль в PEI (у нас це Pcr.sys) ініціалізує DRAM, ми ввімкнемо MMU і Д-кеш. Це логічно. Бо як я писав вище, на армі, - нема MMU, нема Д-кешу. Але вмикати MMU виглядає доцільнішим, коли основна пам'ять ініціалізована. На мурані, її 512 МБ, до речі (DDR3 якшо не помиляюсь).
Стек. Його треба налаштувати навіть для використання в частинах написаних на асемблері, це просто зручно. І його треба налаштувати для частин програми, написаних мовою C, компілятор очікує, шо стек існує.
Шо таке "налаштувати стек"? Звичийано, з інтерфейсом між програмою і C компілятором, можуть виникнути якісь додаткові капризи в цьому, але це не зовсім про стек, то вже про вимоги і очікування компілятора. А от поки ми тільки на асемблері, все спрощується.
На армі, стекові операції робляться за допомогою звичайних store/load пар, з обмовкою, шо це multiple варіанти - вони можуть вантажити в пам'ять і з пам'яти багато регістрів. І так само на армі суть специфікатори режимів для цих операцій, які дозволяють гнучко створювати стеки 4х типів. А саме - тип стеку визначається парою атрибутів, усі можливі комбінації яких дають 4 варіанти стеків. Перший атрибут - це напрямок росту, куди стек росте. Або вгору, або вниз. Тобто якшо наповнення стеку зменшує адресу верхівки стеку - він росте вниз. Це спадний стек (descending), і якшо навпаки, то східнИй (ascending), чи як сказати, мені східнИй подобається. Далі ці два підтипи розбиваються на пару варіантів за другим атрибутом, який визначає, на шо вказує верхівка стеку. Вона вказує або на адресу найвищого зайнятого місця в стеку. Або на адресу найнижчого вільного місця. Тут мабуть під "місцем" треба розуміти одиницю стекової алокації, наприклад слово - воно в армі 4 байти (принаймні в 32-бітному армі, в 64-бітному, логічно одиницєю алокації буде, подвійне слово, 8 байтів). Отже, якшо верхівка стеку вказує на адресу вже покладеного в стек слова - це Повний підтип стеку, якшо на адресу першого вільного місця (розміром в слово), то це Пустий підтип. Поєднуючи один атрибут з іншим, ми отримуємо 4 варіанти стеків:
  • Повний спадний (росте в сторону менших адрес, верхівка (тобто вказівник стеку) вказує на найвище зайняте місце)
  • Пустий спадний (росте в сторону менших адрес, верхівка вказує на найнижче вільне місце)
  • Повний східний (росте в сторону більших адрес, верхівка вказує на найвище зайняте місце)
  • Пустий східний (росте в сторону більших адрес, верхівка вказує на найнижче вільне місце)
Ці варіанти досягаються встановленням порядку оперування на базовому регістрі, який тримає вказівник на стек. Тобто на SP. Хоча можна використовувати і інші регістри. А порядок цей на армі задається спеціальними суфіксами, для відповідної інструкції для push і pop операцій. І ці суфікси відображають оту типізацію - один задає напрямок, другий - пустоту чи повноту.
А саме: є суфікси IA - increment after, IB - increment before, DB - decrement before, DA - decrement after. І є інструкції LDM і STM - load multiple і store multiple відповідно. Параметризація суфіксами яких, дозволяє імплементувати генеричні стекові push і pop операції для всіх можливих варіантів стеків.
Інкремент і декремент прикладаються до базового регістра, тобто до sp тут.
Як саме воно робить? Стек - це структура для сховища, яка оперує за приниципом "перший прийшов, останнім пішов" - тобто дані кладуться один на одне як листи паперу на столі. А знімаються в зворотньому порядку - операція зняття знімає той елемент, який лежить на горі. Кладення це операція PUSH, витягання - це POP. От розгляньмо як зробити Повний Спадний стек. Між іншим - саме цей стек всі й використовують. І ми теж.
Операція кладення на стек:
вказівник стеку (базовий регістр операції, sp) має вказувати на найвище зайняте місце (слово). І стек має рости вниз, тобто в сторону менших адрес. RISC архітектури як ARM, оперують на регістрах, більше ніж CISC, тож в стек можна покласти лише шось, шо лежить в регістрах. Отже, ми маємо діапазон регістрів, які збираємося покласти в стек, тобто вони - джерело даних для PUSH (або - ціль для зворотньої операції POP, звичайно), хай для простоти, діапазон буде з одного регістра. Загально для стеку, операція тоді буде тоді така
push rX
Тобто покласти в стек те, шо лежить в регістрі rX. І здвинути відповідно стековий вказівник звичайно. Для нашого Повного Спадного стеку тоді це буде так:
stmdb sp!, {rX}
stm - store multiple, ми збергіаємо регістр(и) в пам'ять, multiple, бо це на випадок шо регістрів за раз може бути більше одного і майже завсіди так і є (ми спростили до одного, але в діапазоні, вони кладуться по черзі, по номерах, - той шо з найменшим номером попаде в найменшу адресу). Далі суфікс db - decrement before, спочатку відняти - задає той порядок, за який ми казали, спочатку перш ніж класти в стек шось нове, відніми від вказівника на стек. Тобто це значить, шо до цього, він, вказівник, вказував на шось, шо уже зайняте. А тепер ми крокуємо крок вперед. А от куди той "вперед" - в сторону збільшення чи зменшення адрес, задається вибором підсуфіксу - декременту чи інкременту, і визначає напрямок стеку - спадний він чи східний. Справді, якшо ми вибрали декремнтувати - це значить, шо ми прирощуємо стек, зменшуючи адресу верхівки стеку - стек спадний. А повний - бо спочатку ми віднімали від вказівника, тобто здвигали на вільне місце, вказував він на зайняте - повний.
Операція POP: все навпаки. Для повного типу стеку, ми маємо змінити вказівник після завантаження в регістр з пам'яти, бо вказує він на зайняте місце - те, яке ми й маємо витягнути. А напрям для Східного стеку тут має бути інкремент, тобто збільшити, бо ми витягуючи з стеку, зменшуємо його, оскільки росте він в сторону менших адрес, ясно, шо всихає в сторону більших. :^) Отже закодована правильно для нашого типу стеку операція POP буде:
ldmia sp!, {rX}
ldm - load multiple - завантажити з пам'яти в регістр(и), ia - incremet after. Тобто візьми за адресою в SP слово, поклади його в перший регістр, потім збільш вказівник (sp) і роби далі якшо є ще регістри в списку. Або все зроблено.
Отой знак оклику після sp в обох випадках, означає, шо треба зробити write-back в базовий регістр. Тобто, шо поточне значення вказівника підчас виконання операцій має бути записане в базовий регістр. Це наш випадок, бо ми спеціально використовуємо цей регістр (sp), шоб тримати там поточну верхівку стеку. Отже:
Для Повного Спадного:
push: stmdb sp!, {rX} - [b]зменш перед[/b]
pop: ldmia sp!, {rX} - [b]збільш після[/b]
Для Пустого Спадного:
push: stmda sp!, {rX} - [b]зменш після[/b]
pop: ldmib sp!, {rX} - [b]збільш перед[/b]
Для Повного Східного:
push: stmib sp!, {rX} - [b]збільш перед[/b]
pop: ldmda sp!, {rX} - [b]зменш після[/b]
Для Пустого Східного:
push: stmia sp!, {rX} - [b]збільш потім[/b]
pop: ldmdb sp!, {rX} - [b]зменш перед[/b]
:^D Отака потужна система. Очі розбігаються потужно. Зображення Зображення
Через те, шо люди різні, і різні концепції сприймають по-різному, арм далі "спростив", ввівши синоніми до цих операцій, одні заміняють інкремент/декремент і після/перед суфікси, на вказування саме типу стеку, тобто ввів суфікси fd, fa, ed, ea - full descending, full ascending, empty descending, empty ascending. Наприклад stmfd - store multiple full descending, - те саме, шо stmdb - store multiple decrement before - оепрація push для Повного Спадного типу стеку. А також ввів власне синоніми - інструкції push і pop, які мають на увазі саме Повний Спадний тип. Тобто push це те саме шо stmdb, і те саме, шо stmfd.

Так от, ми вибрали Повний Спадний стек. Розмір? Спочатку я думав давати 8 КБ, а зараз вирішив подвоїти. Тобто 16 КБ. Отже, шо таке "налаштувати стек"? Якшо опустити можливі ускладнення з взаємодією з C-компілятором, то це як ми вияснили:
  • вибрати його тип. Вибрали - Повний Спадний.
  • визначити його розмір. Визначили - 16 КБ.
  • визначити його розташування в SRAM.
Тут треба повернутися до карти нашої SRAM. Її, як я вже казав, - приблизно 128 КБ. Вона розбита на дві частини, перші 64 КБ називаються Internal SRAM, другі 64 КБ - L3 OCMC0. Очевидно це пов'язано з її розташуванням внутрі SoC'у, ми про це ще знаємо мало, але обидві доступні нам. Адреса першої підділянки - 402f0000, і вона простягається до 40300000 (не включно). А друга починається з 40300000 і йде до 40310000 (не включно). Перший кілобайт є недоступним (400h) і того перша доступна адреса є 402f0400. А з іншого боку, починаючи з адреси 4030b800 іде ділянка, в яку ром-код понакладав свої дані, свій стек, ішні дані, плюс, і це важливо, - саме десь там лежить таблиця векторів винятків. І хоча ми б могли перетерти те, і забрати собі ту частину, ми не робитимемо цього. Бо хоч в нормальному ході, повернення на ром-код і не має відбутися, на початкових стадіях, воно можливе, ну і зрештою, ми підходимо конструктивно - нам цілком вистачить і наявної пам'яти, тож, не чіпатимемо той ром-кодовий хвіст. Це всього 5 4КБ-них сторінок, якшо вирявняти на сторінку. У ром-коду є ще приватна пам'ять - вона лежить перед цією публічною, і її аж 128КБ - вона доступна лише в Secure Mode. Не знаю, чи належить сюди отой кілобайт, який недоступний, про який я казав. Отже ми маємо карту RAM пам'яти для SEC і PEI фаз, на час, доки остання не ініціалізує DRAM:
  • 402f0000 - 402f1000 - 1 сторінка, 4КБ, пропускаємо, перший кілобайт недоступний, 3 решти опускаємо, шоб вирівняти нашу базу на сторінку.
  • 402f1000 - 402f6000 - 5 сторінок, 20КБ, SEC+mmbfv, сюди ром-код покладе нашу SEC і BFV, який стає mmbfv - відображеним в пам'ять BFV (відтіненим, shadowed).
  • 402f6000 - 40307000 - 17 сторінок, 68КБ, PEI_RAM, сюди SEC грузитиме Pei.exe, а та - свої модулі. Тобто образи з кодом і даними.
  • 40307000 - 4030b000 - 4 сторінки, 16КБ, SEC_PEI_STACK, наш стек іде сюди, Зображення тобто внизу площі, якою ми себе обмежили.
  • 4030b000 - 40310000 - 5 сторінок, 20КБ, ROM_CODE_AREA, та ділянка, зайнята ром-кодом, про яку я казав, і казав, шо ми її не чіпатимемо.
Тобто ми обмежили себе ділянкою, куди ром-код каже, він грузе своє навантаження, тобто нас, нашу фірмварю, ця ділянка прописана з 402f0400 по 4030b800, де починається "публічний стек", як ром-код це назвав. Хоч там шо він мав на увазі, Зображення це розділення на публічний і приватний відображає наявність Secure Mode, тут нам недоступного нажаль. До речі, наш стек і стек ромкода лежатимуть по сусідству. Зображення Якшо глянути, ми взяли той діапазон, зарівняли його з обох країв на сторінку вниз (тобто скинули а не вилізли), і сіли там, де він каже, шо він туди може грузити. Це логічно - ми таким чином відкидаємо будь яку можливість попсувати шось, шо ми не клали. Хоча документація не каже, шо ми не можемо вилізти і потерти отой хвіст. Але як я вже наводив - це краще, не вилазити.
Дивіться, з таким вибором і вирівнюванням, ми маємо аж 26 сторінок, тобто 104КБ, де працювати, поки не ініціалізуємо DRAM, і не переліземо туди.
Отже, роздивившись нашу карту, ми вже знаємо і де буде наш стек. 40307000 - це найнижча адреса куди можна писати в стек. Повертаючись до нашого опису типів стеку і того, який ми вибрали, ми маємо, шо спочатку він в нас вказуватиме на перший байт поза своїм дном, - адресу 4030b000. Перше ж кидання на стек, зменшить цю адресу на 4 спочатку, і потім покладе на адресу 4030affc наше слово. От вам Спадний Повний стек. Зображення А коли стековий вказівник дійде до 40307000, то це буде stack overflow. Неможна цього. Зображення Наче розібрались.
Тож наша ініціалізація стеку, записана в армівському коді, виглядатиме якось так:
	/* setting up stack */
	ldr	sp, ho.StackBase
	add	sp, sp, #(AM3358_STACK_SIZE)
Перша інструкція (ldr - load register, завантажити регістр) завантажує слово, яке знаходиться в позиції символа ho.StackBase - це член структури, який містить базу стеку, ми маємо її також передати до точки входу в PEI, тож ми заливаємо цю структуру в SEC фазі, і беремо звідти значення для обрахунків. А друга інструкція додає до цієї бази (яку ми завантажили в стековий вказівник, sp) визначену нами величину, яка ховається за читабельним і змістовним іменем AM3358_STACK_SIZE. Препроцесор розкриє секрет цього імени, показавши, шо там лежить, а лежить там, те, шо ми туди поклали в заголовку Sec.h:
#define AM3358_STACK_SIZE	0x4000			/* 4Pg, 16KB */
Насправді, перша інструкція, так як вона записана вгорі, це один з синонімів - арм дуже любить "позиційно-незалежне адресування", і того все хоче адресувати PC-відносно, тобто відносно до програмного лічильника (program counter, PC). І хоч символ ho.StackBase - це власне адреса (спочатку відносно до секції, де символ визначено, потім просто адреса), але асемблер бере цей ярлик як вхідну інформацію, і обраховує відстань (позиції) цього символа до PC, тобто до адреси (в тому самому просторі, шо й адреса символу) поточної інструкції, яка власне звертається до символа. Тобто ота перша інструкція - це повний еквівалент, а отже синонім до такого запису:
ldr sp, [pc, #(ho.StackBase - . - 8)]
От шо тут неінтуїтивно в першому варіанті, так це відсутність квадратних дужок. Квадратні дужки в багатьох асемблерах означають розіменування, тобто візьми те, шо в дужках як адресу, і сходивши за нею, завантаж в цільовий регістр вміст тієї ділянки пам'яти. От цей другий варіант, бачите, слідує цьому. А той синонім - ні. Трохи збиває з пантелику. Зверніть увагу на вираз внутрі #() в другому варіанті. Це дуже важливо. Раз арм хоче PC відносну адресацію, ми маємо її йому дати, отже ми беремо як базовий регістр регітср pc (синонім r15). Через кому має йти вираз, який означає відстань від PC (яку треба до нього додати/відняти, шоб отримати нарешті адресу ділянки пам'яти, з якої треба читати). Тобто відстань від нашої інструкції, до нашого символа, в якому лежить значення нашої бази стеку (символ ho.StackBase). Отой тричлен має адресу самого символу (значення символів в асемблерах - це їхні lvalue, тобто їхні адреси, не значення, які лежать за тими адресами (rvalue)). Далі ми віднімаємо "точку", шо означає поточний лічильник в секції, спрощено кажучи - це адреса поточної команди, воно так і є, але це адреса - відносно до адреси секції. Але перший символ також так само - відносна до початку секції адреса, тобто різниця буде правильною, і буде відстанню між цими елементами. А оте мінусування 8-ми виглядає як WTF? Чи не так? Дійсно, шо ми отримаємо якшо віднімемо від адреси символа, адресу команди, яка до нього звртається? Ми вже взнали - відстань між ними. Так це і є те, шо треба, шоб додавши до PC, отримати потрібну адресу нарешті. Нашо тоді мінусувати ще вісім? А це того, шо на процесорі арм архітектури, коли він виконується в арм режимі (а ще є тумб і тумб2 і тумбЕЕ - останнє закинуте), в регістрі PC, на момент виконання якоїсь інструкції, зберігається адреса не поточної інструкції, а на дві інструкції вперед, тобто на 8 байтів вперед (на армі в арм режимі, інструкції завсіди 4-байтні). Отакий льохкий каприз. Зображення Думаю, може оце моє розжовування комусь пригодиться. Того й писав так докладно.
Отже, ми грузимо базу стеку в стековий вказівник, а потім додаємо до нього розмір нашого стеку. Таким чином, ми з'їжжяємо на дно стеку - він у нас повністю пустий. Але не за типом, а за фактом. За типом він у нас повний. Зображення Тепер ми можемо використовувати стек. Наприклад, якшо нам треба зберегти регістри r6, r7, r8, і lr - лінковий регістр (синонім r14), ми напишемо:
stmdb sp!, {r6-r8, lr}
дефіс це скорочена форма задавати суцільний діапазон номерів. lr (r14) іде розривом, того вказується окремо. Ця інструкція покладе по порядку в стек наші регістри (їхній вміст звичайно), а порядок цей, я вже казав, - вміст регістру з найменшим номером попаде в найменшу адресу. І також, ця інструкція виставить sp, стековий вказівник, відповідно, записавши це назад в sp. А коли нам уже буде треба витягти їх назад, ми напишемо:
ldmia sp!, {r6-r8, lr}
І ця інструкція зробить все навпаки. Дуже важливо не наплутати з діапазоном регістрів. Так завсіди з парними дзеркальними операціями - malloc/free, open/close наприклад. Діапазон має бути той самий. Ну це очевидно. Зображення
Тобто ми не лише побалакали детально за стек, ми ще й охопили опис пам'яти наших перших двох фаз. Ще треба вияснити, чи є така необхідність робити їм ділянку динамічної пам'яти (купу тобто). Чи можна обійтись. Це ще не вирішено. Але якшо купа буде, то, за завантаженими образами і перед стеком. Але навряд в такому обмеженому середовищі це буде доцільно. Модулям мабуть доведеться обмежитися своїми секціями даних і стеком. Просто специфікація ще диктує забезпечувати певні сервіси, які мають бути доступні для PEIM'ів (модулів PEI фази), і ці сервіси має надавати Pei Foundation (наш Pei.exe). Там наче як є сервіси типу AllocatePool(), FreePool(), ну назви явно натякають. Не уявляю, куди тут можна вткнути менеджмент динамічної пам'яти, але треба розбирати. Особливо цікаво, куди його втикати на 14КБ міпса криейтора. Зображення
Це звичайно не все, шо хотілось би висвітлити в цьому блоці, є ще конвенція викликів - які регістри як берегти, як передавати аргументи функції тощо. Це пов'язано з описаним вище. Також логічно було б описати трохи місію PEI, і її розкрій, як ми його собі надумали. Але я боюсь, шо допис не влізе в певний виставлений ліміт. Смайлики он точно вже вилізли, прийдеться іти викреслювати смайлики. Яка дурість - обмежувати смайлики! Тож, побалакаємо за згадане наступного разу.

Повідомлення відредагував _Ex: 25.09.2016 – 02:46

  • 0

#14 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 01.10.2016 – 22:57

Отже я обіцяв балакати "предметніше" за SEC фазу. Ну от, нашкрябавши трохи коду (на асемблері! Зображення) спішу похизуватися
виставити себе на посміх. Зображення Ну справді, досвіду писання на асемблері 7-го арма в мене малувато, отже може мій код і ламерський. Покишо це далеко не повна імплементація SEC фази, і тим паче не тестована, то ж "найцікавіша" частина - пробувати запускати машину змушуючи її виконувати твій просякнутий помилками код. Уявіть, нема ні ОС, ні фірмварі (BIOS'а), мій код і є фірмварею! Зображення В майбутньому.))) Власне той код, який я поки написав, єдине шо робе - просто збирається (компілюється). Його ще перевіряти і перевіряти.
Отже ми казали, шо безпеку ми поки викинули з розгляду, тож її бітів нема. Далі, є така штука як "reset reason" - причина ресету - чого саме компутер зазнав перезапуску. Тут починаються нетрі ACPI, і просто кажучи, починаючи з певної фази, треба вміти розрізняти цю причину ресету, і відтак іти різними кодовими шляхами. На SEC фазу це мабуть не дуже вплине, на PEI мабуть все почнеться, але розібратися варто глибше. А поки наша SEC фаза поводиться як наче ресет завсіди один і той же - повний старт, холодне завантаження.
Шодо ініціалізації/налаштування PLL, тактових сигналів, то, як і очікувалося, ця складна фігня не вимагає аж зразу все робити так рано, користуймося поки тим шо і як ром-код наладив. Про MMU, кеші і вгадувача галужень я вже розказував. Коротко - MMU вимкнене, D-кеш вимкнений, I-кеш і вгадувач галужень ввімкнені.
Обробники винятків. Не написані ще. Знову, шо ми зараз можемо там в тих обробниках зробити? Хібашо поміняти ром-кодовий dead-loop з ресетом на повне вимикання (shutdown). Має сенс, оскільки, якшо наприклад виникає Data Abort і ром-кодовий обробник просто чекатиме на ресет, то машина зациклиться, повторюючи ту саму помилку, яка викликає аборт. Вимикання - це наш рятівник на цих ранніх кроках - він нам і сигнал і помічник уникнути ахтунгу зациклювання. Немає ще нічого, навіть виводити нікуди виведення. До UART'а ще як до Києва рачки. Тож як у випадку з внутрішніми невдачами в SEC, ми стрибаємо на вимкнення (це буде видно з прикладу нижче), так і тут, в обробниках, максимум, шо ми покишо можемо - зробити вимкнення. Обробниками займатимемося в найближчий час, тож тоді я розкажу за них.
Нарешті лишається завантажити Pei.exe з mmbfv. Це мабуть найпростіша частина, і оскільки я ось недавно возився з FV, пишучи ту форматувальну утиліту (дивіться вище), я приклався до практичної імплементації SEC фази саме звідсіля. Власне оцей шматочок і хотів продемонструвати. Небагато.
Отже ми налаштували стек, і тепер готові завантажити Pei.exe і передати туди керування. Файл лежить в mmbfv - відображеному в пам'ять BFV, ми маємо знайти його там, розпарсити PE структури, і завантажити секції коду і даних, відповідно зробивши кеш-підтримку, пов'язану з цим, і передати керування на точку входу в цьому файлі - PeiEntryPoint(). Ось шматок коду, який шукає файл в томі, робить певні перевірки, і в кінці має вказівник на область на сам файл, і його розмір, який тепер треба розбирати як PE образ.
Загалом писати на асемблері арма нормально. Мені сподобалось. Зображення
На кінець - сам код (частина його, без даних і початка, про стек я вже розказував і показував вище), оригінальні коментарі я поприбирав, замінивши на підходящіші для цього опису, навряд вийшло дуже ясно, але я наводю код для ілюстрації, шоб ви мали уявлення як вого виглядає.
Прихований текст

Повідомлення відредагував _Ex: 01.10.2016 – 23:26

  • 0

#15 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 05.10.2016 – 00:57

Цитата

Отже ми маємо карту RAM пам'яти для SEC і PEI фаз, на час, доки остання не ініціалізує DRAM:
  • 402f0000 - 402f1000 - 1 сторінка, 4КБ, пропускаємо, перший кілобайт недоступний, 3 решти опускаємо, шоб вирівняти нашу базу на сторінку.
  • 402f1000 - 402f6000 - 5 сторінок, 20КБ, SEC+mmbfv, сюди ром-код покладе нашу SEC і BFV, який стає mmbfv - відображеним в пам'ять BFV (відтіненим, shadowed).
  • 402f6000 - 40307000 - 17 сторінок, 68КБ, PEI_RAM, сюди SEC грузитиме Pei.exe, а та - свої модулі. Тобто образи з кодом і даними.
  • 40307000 - 4030b000 - 4 сторінки, 16КБ, SEC_PEI_STACK, наш стек іде сюди, Зображення тобто внизу площі, якою ми себе обмежили.
  • 4030b000 - 40310000 - 5 сторінок, 20КБ, ROM_CODE_AREA, та ділянка, зайнята ром-кодом, про яку я казав, і казав, шо ми її не чіпатимемо.
Невеличке уточнення сюди. Це закономірно - оскільки описується процес створення. Перше, все ж було вирішено не викидати (для вирівнювання на 4КБ) перші доступні 3КБ. Це того, шо ми послабили вирівнювальні вимоги для всієї SEC та PEI фаз, з 4096 до 256. Тепер можна забрати перші 3КБ. Зображення MMU всеодно вимкнене тут, а саме для його програмування (задавання атрибутів сторінок), нам було б зручно оперувати сторінками, отже - виділяти якісь базові блоки вирівняними на сторінку. Оскільки тут поки цього нема, це дає нам можливість ще компактніше розмістится в пам'яті. Коли ж MMU стане ввімкнене, ми ці ділянки зробимо тільки на читання і виконуваними. Це логічно, бо тут код, а дані, хоч і писальні в час виконання цих фаз, на момент ввімкнення MMU (а це кінець Pei фази), точно будуть лише читальними. За фактом. Тобто їх наступні фази можуть використовувати лише для читання. Їм не треба буде туди писати.
Також шодо формату Pei.exe. В UEFI PI специфікації, на додачу до основного PE32 (для 32-бітних машин) і PE32+ (для 64-бітних), задається ще такий формат - TE, terse executable. Це PE32+ з значно обмеженою кількістю метаданих. Тобто багато полів заголовків, DOS заглушка викинуті, замість цього використовується один заголовок для образа, в якому зібрані поля з повного формату, лише ті, які потрібні для ранніх фаз ФВ. Різниця в приниципі помітна але не критична. Наприклад, в повному PE32+ форматі, розмір основного заголовку - Опціональний Заголовок (Optional Header) - 240 байтів, - розмір заголовку TE образу - 40 байтів. Максимуму економії ми досягаємо тут на тому, шо ці зайві в цьому випадку метадані не забиратимуть місце в BFV а отже - в mmbfv, це важлива економія для випадку mips creator ci20 (машина "йод"), там всього 14КБ доступного SRAM.
Тепер карта пам'яти виглядатиме так:
Карта RAM пам'яти для SEC і PEI фаз, на час, доки остання не ініціалізує DRAM:
  • 402f0400 - 402f1000 - 3КБ, використовуємо, вирівнємо тепер на 100h. Це FW_START, SEC_BASE.
  • 402f1000 - 402f6000 - 5 сторінок, 20КБ, SEC+mmbfv, сюди ром-код покладе нашу SEC і BFV, який стає mmbfv - відображеним в пам'ять BFV (відтіненим, shadowed), і також сюди ми завантажимо Pei.exe
  • 402f6000 - 40307000 - 17 сторінок, 68КБ, PEI_RAM, сюди Pei.exe, грузитиме свої модулі. Тобто образи з кодом і даними. І купа тут, за образами.
  • 40307000 - 4030b000 - 4 сторінки, 16КБ, SEC_PEI_STACK, наш стек іде сюди, Зображення тобто внизу площі, якою ми себе обмежили.
  • 4030b000 - 40310000 - 5 сторінок, 20КБ, ROM_CODE_AREA, та ділянка, зайнята ром-кодом, про яку я казав, і казав, шо ми її не чіпатимемо.
Або трохи по іншому, відображаючи "бази" - де лежатимуть наші об'єкти (пара адреса - розмір):
  • 402f0400 - SEC_BASE, 800h (2048 Б) - SEC фаза, код і дані в купі.
  • 402f0c00 - BFV_BASE, 4000h (16384 Б) - mmbfv відтінений в пам'ять BFV
  • 402f4c00 - PEI_CORE_IMAGE_BASE, розмір - 1400h (5КБ) - Pei.exe завантажений в пам'ять на виконання, розмір тут неостаточний, він визначатиметься на ходу. Тут задана для зручности, шоб показати адресу початку доступної для Pei фази пам'яти, вона залежить від цього розміру.
  • 402f6000 - PEI_RAM_BASE. Розмір - 11000h (68 КБ), але треба пам'ятати, шо він вираховується як різниця між STACK_BASE і PEI_RAM_BASE, остання залежить від розміру образу Pei.exe. Це та пам'ять, яка доступна для Pei фази, і куди вона вантажитиме PEIM'и, і де буде купа.
  • 40307000 - STACK_BASE, 4000h (16384 Б). Стек, тут все як було
  • 4030b000 - ROM_CODE_AREA, 5000h (20 КБ)
Ми змінили вимоги вирівнювання. Як я вже казав. В нормі, ми матимемо для PE32 образів FileAlignment в розмір сектору для сховища, тобто 512 байтів тут, а для SectionAlignment - в розмір сторінки в пам'яті, тут це 4096 байтів. Перше задає вирівнювання секцій в файлі, друге в пам'яті. Наприклад саме так збиратимуться усі модулі Dxe фази, власне вся фірмваря, яка ганятиметься в DRAM. Але для SRAM, тобто для Pei модулів, ми послабили вимоги, і вирівнюємо базові адреси на 256 байтів. Базу образу, базу коду, вирівнювання секцій наприклад. Це для раціональнішого використання дуже малої SRAM. Тож, наш виконуваний файл Pei.exe, який міститиме Pei диспетчер, а також сервіси, матиме такі вимоги:
ImageBase - 402f0c00, CodeBase - 100h, отже секція коду має бути завантажена в 402f0d00, а значить це має бути адресою функції PeiEntryPoint(), яку ми в кінці Sec фази викликаємо. Ну не обов'язково ця функція має лежати на самому початку секції коду, обов'язково було б лише у випадку XIP варіанту, тобто NOR флешу. У нас його нема. Але ми покладемо точку входу на початок секції коду. Зображення
Які модулі будуть в Pei фазі? Я вже не пам'ятаю чи я розказував за це, лінь перевіряти, розкажу ще раз значить. Зображення Логічно, функціональні блоки представлені інтерфейсами - PPI, навколо них багато чого крутиться. вони експонують якусь функціональність, або стан. Від їхньої присутности чи відсутности (інстальованости/неінстальованости в БД Pei Фундації) залежить які модулі диспетчер ставитиме на виконання а які чекатимуть ще. Там ціла мова залежностей. З своїм набором інструкцій! Стекова машина.
А імплементують ці інтерфейси модулі, дрйавери Pei фази власне. Оскільки один модуль може імплементувати кілька інтерфейсів, кількість модулів буде менша за кількість інтерфейсів. Особливо це стосується нашого варіанту ОПК, де ми маємо SoC базовану платформу - багато функціональних блоків пристрою представлено на одному чипі, вони завсіди ідуть разом - логічно тоді поєднувати код підтримки всього цього в один модуль. До чого я й веду. Ми матимемо принаймні 1 модуль. Якшо Pei.exe імплементує архітектурно незалежні речі, визначені самою специфікацією, тобто логіку самої фази - диспетчер, сервіси, які служать для полегшення роботи модулів, то модулі - це дуже платформноспецифічні компоненти, які займаються налаштуванням і підтримкою власне пристроїв. Тобто це драйвери. Ми маємо налаштувати процесор (пам'ятаєте, після ініціалізації DRAM, ми маємо ввімкнути MMU і D-кеш, це як мінімум, і це, шоб ви розуміли, не коники з гімна ліпити Зображення), ми маємо налаштувати "чипсет" - тобто набір кореневої логіки, PLL оті бісові, і решта PRCM (power, reset, clock managemnt - це такий модуль в Sitara am3358) принаймні те, шо треба для ФВ, можливо навіть треба налаштовувати модулі "системної" шини, interconnect'а, мости всякі, хз чи треба буде їх налаштовувати, ось ще контроллер переривань (бо хоч ФВ не займається обробкою всіх переривань, але UEFI каже, шо переривання мають бути дозволені, бо одне переривання обробляти треба точно, серце системи, - переривання від системного таймера, системний таймер має цокати), також сторожовий собака, багато чого іншого (наприклад я затрудняюсь вам сказати, чи треба буде тут налаштовувати якось DMA - direct memory access, якшо так, то це теж "чипсет"), і, нарешті, ми маємо ініціалізувати DRAM - на мурані, в SoC'у є контроллер пам'яти, він називається EMIF, плюс може прийдеться шось возитися з такою устрашаюшшьою річчю як DDR PHY контроллер. Зображення Ось оце все, плюс все, про шо ми не підозрюємо, але воно теж сюди ліпиться і вимагає уваги, ми кладемо в один PEIM, який ми обізвали Pcr.sys. Processor, chipset, ram. .exe розширення ми даємо "основним" модулям фаз, а .sys - драйверам.
Також, архітектурно, в самому кінці, Pei фаза має виконати спеціальний PPI - Dxe IPL. Dxe Initial Program Loader, завантажувач решти ФВ. DXE - це Driver Execution Environment, середовище виконання драйверів. Це власне сама ФВ. А IPL - її завантажувач. Я вже за нього згадував раніше. Так от, до питання скільки буде модулів, поки я не знаю, чи цей PPI буде вшитий в Pei.exe чи виокремлений в окремий модуль. Якшо останнє, то він зватиметься DxeIpl.sys (сурпрайз! Зображення). Це трохи про вміст Pei фази, а значить і BFV.
Пізніше, ми детальніше роскажемо, шо робе Pei. Шо це за диспетчеризація. Коли робитимемо її. Зараз ми згадали коротко, бо згадали карту пам'яти, і шо куди вантажитиметься. Бо це ж ми вивчаємо PE формат в рамках продовження писання Sec фази, ну і були внесені певні поправки. Після того як наша Sec научится вантажити Pei.exe, перейдемо до обробки армівських винятків. Після цього, треба вернутися на початок нашого коду і поглибится в ввімкнення I-кешу і вгадувача галужень, бо ми опустили це, вмикати не складно, там є нюанси пов'язані з передвитяганням (prefetch) і з приведенням в консистентний стан. Шоб не вийшло після вмикання упсі ситуацій. Власне ром-код каже, шо він лишає I-кеш і ВГ вимкненими, це має полегшувати справу, але хз. Там основна проблема не в регістр системний писнути, вмикаючи ці дві сутности, а чітко і повністю розгребти усе плетиво пояснень в армівському манулі. Як воно робе і шо треба робити і шо не треба робити (насправді все добряче пересипане не лише поняттями, які спливають швидше, ніж ти можеш усвідомити, шо це все треба розуміти значно глибше, ніж пробігти очима по тексту з їхнім описом, але й купою "якшо", тобто розгалужень ситуацій, тож можна уявити як воно - зрозуміти це. Зображення Але зрозуміти треба). Прикол в тому, шо там основне "париво" - це багатопроцесорність, у нас її немає ні фізично ні логічно, SoC однопроцесорний, а ФВ робе на одному процесорі всеодно. Але зрозуміти треба і для нашої ситуації, а описано на всі випадки. От і розгрібай всі ті PoC і PoU, і решту некогерентної фігні про когерентність. Зображення Оце такий план. Ми ще рішили впхнути shutdown сюди, казали вже - зручний спосіб виявити, шо твій код спрацював, і також - уникнути дуже неприємних зациклювань. Тож, всюди, де ми стикаємося з "фатальною" помилкою, яка не дає рухатися далі, ми кладемо шутдавн, і також ми його кладемо всюди, де мало б бути шось далі, але його нема. Зображення Наприклад, написавши Sec фазу, ми мали б передати керування Pei фазі, але оскільки там ще нічого нема, ми в PeiEntryPoint() викличемо наш FatalError() (дивись допис вище), і матимемо таким чином можливість перевірити Sec фазу без необхідности мати Pei фазу. З вимиканням всієї плати все не дуже просто. На платі є спеціальний чип для цього, PMIC, - він займається живленням всієї машини і є програмованим і вміє в тому числі вимикати. Але з процесора до нього можна достукатися лише по I2C шині. Тобто сам по собі цей живитель-годівник всіх і вся, сидить в глибокій периферійній жопі. Зображення Халепа. Я не впевнений, шо вона в наш час взагалі доступна і ввімкнена. Тобто дьорті хак з писанням в потрібний регістр PMIC'а покишо відкладається. Зображення Є ще надія на PRCM, цей удад сидить в SoC'у і має бігати в наш час. Є надія, шо його можна якось попросити вимкнути це все швидко. Але покишо складається враження, шо з ним ми можемо лише ресетнути. Це не те. Та й сторожовий пес би ресетнув через 3 хвилини і так. Нам же треба зрозуміти, шо ми отримали керування. Потрібна якась інша дія, ніж ресет. Тобто це ще одна точка виснажливого вовтуження з 5000-но сторінковим манулом. Тепер уже від TI. Так так, обидва вищезгадані мануали саме тягнуть кожен по 5000 сторінок. А ви кажете я багато пишу. Зображення
І от коли оце буде зроблено, ми матимемо першу готову до пусків фазу ФВ. Зображення

Повідомлення відредагував _Ex: 05.10.2016 – 01:20

  • 0

#16 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 06.10.2016 – 03:26

Маємо ще одне уточнення в нашу будову. Якшо ви слідкували, то вже знаєте - наша Sec фаза планувала завантажувати виконуваний файл дальшої фази (Pei) з відображеного в пам'ять фірмварного тому (BFV). По суті це було б копіювання - ром-код записував би BFV в пам'ять, без огляду і гадки шо там, а Sec витягала б звідтіля образ виконуваного файлу, і копіюючи його вміст десь поблизу, створювала б нарешті образ в пам'яті.
Ідея зробити готовим для виконання сам файл в ФТ розглядалася, але була відкинута як нереалістична. Тепер, заглиблюючись в тонкощі завантаження образів і знаючи точніше устрій наших виконуваних файлів, ми, стимульовані прикладами ззовні, знову звертаємось до цієї ідеї. Цей варіант має беззаперечні переваги. По-перше, він - зеро-копі так би мовити - завантаживши один раз шось, ми не копіюємо його для подальшого використання. Звідси помітна економія на місці, згадайте як критично це буде для йоду, де ми матимемо всього 14КБ доступного сраму. По-друге, це виграш в швидкодії також - нам зменшується роботи для приготування до передавання керування наступній фазі. Взагалі, якшо оптимізація дає виграш і по простору і по швидкодії разом, то це або справді афігенна оптимізація, або це помилка і нічого не працюватиме взагалі. Зображення Маємо надію - це саме перше.
Як воно робе?
Це власне трюк, який трактує ситуацію як XIP сценарій. В останньому, ми маємо пристрій постійного сховища, який може бути адресований через фізичний системний простір - тобто ми маємо NOR флеш. Тоді ми спеціально все там кладемо так, як би воно було в RAM, готовим для виконання, і, після ініціалізації (ром-кодом) NOR флешу, ця ділянка стає адресованою прямо процесором. NAND сховища мають свій адресний простір і не можуть бути адресовані прямо. Того вміст NAND'у спочатку відтінюється в пам'ять. Як і жорсткі диски і інші постійні пристрої сховищ. От ром-код скопіював нам нашу SEC фазу і заодно маленький BFV том, в якому лежать наступні виконувані модулі. І воно стало mmbfv - відображеним в пам'ять. Тобто його вміст можна доступатися прямо адресуючись процесором. Треба просто знати базу тому і шо де лежить. Спочатку ми думали використовувати це лише як кеш. Схоже до системного файлового кешу ОС. Але тепер, ми рішили, піти далі, і використовувати його як XIP сховище. Тобто сховище, яке містить код, керування на який можна передати без усяких совань і розсовувань внутрі (вони були б просто неможливі для ФТ).
Коли ця ідея була відкинута вперше, мотив був шо секції виконуваного файлу в файлі і в пам'яті лежать неоднаково. Тобто в файлі секція коду лежить вирівняною на файлове вирівнювання і зарівняна на нього ж, а в пам'яті - на секційне вирівнювання. Цей факт і справді є стоп-фактором для XIP. Подумайте, адреса якоїсь змінної виписана в коді (лінкувальником) так як воно має бути в пам'яті, але в фалі, по суті ці сутності можуть лежати ближче, просто кажучи відстань між початком однієї секції і іншої різна для файла образу і для виконуваного образа в пам'яті. Але. Це не обов'язково має бути так. Зазвичай секційне вирівнювання (тобто в пам'яті) вибирається на архітектурну сторінку. Тобто 4КБ майже всюди. В альфи 8КБ і в Ітаніума 8Кб. Наприклад. А файлове вирівнювання зазвичай іде на розмір сектора, тобто переважно 512Б. Але можна зробити ці вирівнювання однаковими. Зазвичай це коли воно береться менше, ніж архітектурний розмір сторінки. Тоді розкрій в файлі вже майже такий як буде в пам'яті (майже, бо не все, шо є в файлі, кладеться в пам'ять, але відстані того, шо кладеться тепер однакові в обох випадках, і це важливо). Цей сценарій - реальний і годиться для XIP. Чим треба пожертвувати? Є такі секції, .bss секції - секції для неініціалізованих даних. Вони містять змінні, вміст яких визначатиметься з самого початку лише з моменту роботи програми, тобто вони неініціалізовані. Такі змінні зручно групувати в одну секцію і цілком викидати її з образу, лишаючи там лише інформацію про її розмір. Це вигідно для економії розміру образа. Ця секція матеріалізується лише в пам'яті. Тоді завантажувальник створить її, і заб'є нулями її комірки. Програми ж уже писатимуть туди, шо вони хотіли. Так от. За XIP сценарію від цього треба відмовитися. Чого? Бо ми нічого не можемо розсовувати і додавати в файл. Файл має бути готовий для виконання. Отже або класти ці нулі одразу в файл. Або просто зробити одну секцію для даних і перемішати ініціалізовані і неініціалізовані дані. Це невелика жертва. Саме так робе наша SEC фаза - там взагалі покишо лиш "літеральний пул" в кінці секції коду як сховище для даних. А виграш звичайно великий.
Як це вплине на завдання для SEC фази, нашої поточної роботи? А от уявіть - воно полегшує її! Ось вона - справжня оптимізація. Зображення Вона всім все полегшує. Тепер нам не треба копіювати PE файл. Ми його вже найшли, треба зробити ще трохи перевірок, пройшовись по сутностям PE образу і передавати керування - все уже украдено завантажено до нас. Зображення
Тут було ще одне заперечення - як вибирати адреси (ImageBase) для образів в такому сценарії. Виглядало, шо це дуже негнучко - вгадувати, яку ж адресу треба взяти. Але, подумавши, воно вияснилось - нема там ніякої негнучкости. Просто треба це робити не вручну перед кладенням в ФТ, а консистетно на час побудови фірмварного тому. Тобто ось воно - хто обслуговуватиме нам цю вигоду - утиліта, яка будуватиме ФТ, має робити релокацію образам. Але це вигідно. Бо робити це утилітою на час побудови - мабуть зручніше, ніж робити релокацію в самій ФВ, еге ж. І всеодно, як явже казав - нам всеодно треба створювати таку утиліту, яка вмітиме оперувати на файлах в ФТ. Просто тепер до тих суто файловосистемних операцій додається ще специфічна для виконуваних образів. Але робити то там - найзручніше. Ми кладемо виконуваний файл злінкований на 0 спочатку в ФТ і кажемо утиліті базову адресу, де цей том буде відтінений, і вона вираховуючи куди покласти наш файл, робе йому релокацію. Повна аналогія з XIP сценарієм. А адресу куди наш BFV відтінятиметься в SRAM, ми знаємо также достовірно як адресу Sec. Власне вони ідуть один за одним, і ми вже використовували наші достовірні припущення про адреси ФТ, коли шукали файл в ньому. Тобто з Sec і Pei трохи знімається, на будувальну утиліту - додається.
Отже підсумок. Ми вирішили, передавати керування на образи прямо в mmbfv, замість копіювання їх. Це кладе певні обмеження на нас, але дає набагато більше вигод. Ми знехтували цим на початках через недогляд. Вовтуження навколо звязків між TE і PE наштовхнуло нас повернутися до цього класного варіанту. Це міняє карту, обговорювану вгорі, в той бік, шо тепер всі адреси за останнім байтом mmbfv - вільні для використання для купи і стеку. Образи уже оприходувані. Зображення
З CFV, наступним ФТ, в Dxe і далі, тобто працюючи вже в DRAM, ми робитимемо як робе ОС - тобто вантажитимемо образи як зазвичай, не трактуватимемо фірмварний том як XIP. Там інші реалії, і навіть якшо знову, ми завантажимо весь том в пам'ять (тобто створимо mmcfv), його вже вигідніше буде використовувати лише як ридонли кеш, і вирівнювання файлове і секційне вже не збігатиметься - тут ми уже економитимемо на розмірі кешу. Або взагалі стане ясно, шо це невигідно - кешувати увесь CFV том. Він же більший. Тоді пряме завантаження образів з носія стане єдиною опцією.
  • 0

#17 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 08.10.2016 – 02:11

Ну і яка ж навчальна/описувальна писанина без ілюстрацій? Того ось кидаю ілюстрацію для візуалізації обговорюваного. Там видно карту SRAM для машини муран1 (Beagle Bone Black, SoC TI Sitara am3358, Cortex-A8, x1, 1GHz), і видно розкрій цієї карти - шо і де лежатиме, який має розмір, - стек, купа, XIP ділянки внутрі mmbfv, Sec фаза - все, за шо ми балакали тут останнім часом. Звичайно, показані розміри окремих файлів Pei фази і їхнє позиціонування внутрі mmbfv - довільні. Треба розуміти, шо вони десь там лежать внутрі mmbfv і мають якийсь розмір. PE32 секції їхні справді суцільні, не порвані. Тож якшо намальовано секцію .text (код) файлу Pei.exe, це означає, шо та виділена кольором ділянка справді йтиме суцільно. Одиниця шкали на правій, деталізованішій частині розкрою, - сектор, 512 Б, на лівій - сторінка пам'яти, 4096 Б, 4 КБ.
Зліва стовбчик рожеивм кольором - адреси SRAM ділянки в фізичному просторі (для фірмварі VA == PA, і "віртуальний" простір всього один - фірмваря це однопроцесна однопотокова система навіть на багатопроцесорних машинах, тут машина однопроцесорна енівей). Ліва лінійка - вся (доступна в Non Secure стані) SRAM ділянка, яка складається з двох реґіонів (забув це відобразити на малюнку, звиняйте. Зображення) - Sram Internal, 402f0000-40300000, 64KB, і L3 Ocmc0 SRAM, 40300000-40310000, 64KB. Справа дається деталізованіше ділянка, в якій уся наша фігня крутиться - SEC і mmbfv, з якого ми "витягаємо" (тепер уже лише логічно, дивись уточнення шодо XIP в попередніх дописах) PEI фазу. Решта позначок розкрою (аранжементу якшо хочете), кольорового кодування і підписів, має бути самоописувальна і зрозуміла.
(Робив Маестро Пейнта. Чого англійською? Того шо ця картинка може знадобитися для міжнародної авдиторії!)
Зображення


---------------
1 - я увесь час згадую "цільові машини", "парк машин", "цільові архітектури", навіть імена цих машин згадую. А конкретніше за це не написав. У читача може скластися (хибне) уявлення про велику кількість цих машин і про дебеле різноманіття архітектур/платформ в цьому моєму парку. Отже варто трохи роз'яснити за це.
В рамках цього проєкту планується створити UEFI імплементацію всього на кілька архітектур. А точніше - на ARM і MIPS. В обох випадках і на 32-бітні і на 64-бітні варіанти. Але варто уточнити - шо 64-бітний MIPS мені не доступний, тож це просто дуже футуристична ділянка. Ці дві архітектури складають так звану "першу хвилю" проєкту. Яка в свою чергу розбивається на чергу - найпершою іде однопроцесорний варіант ARMv7, 32-біти ясно. Далі - багатопроцесорні машини ARMv7 і тут же - MIPS, 32-бітні. Ми маємо всього одну MIPS машину, тож тут не особливо розгорнешся з вибором версій і бітностей, я писав про цю машину - мені її подарували. Класний чувак з фірми Imagination, яка володіє MIPS'ом. Він там уже не робе до речі. Отже яка там версія архітектури, таку ми й підтримуємо. А це mips32r2. У кожної архітекури свій варіант називати версії і ревізії, а також своя історія. у міпса вона довга. Ця архітектурна версія десь з 2002-2003 року. Тож середньонова/стара. А далі в черзі іде 64-бітний ARMv8. Ну і якшо появиться шось 64-бітне міпсівське, нарешті, - і воно теж. Міпс відстає тут від арма. Хоча має 64-бітні версії вже стільки років. Просто зараз на ринку ОПК є всього одна (!) міпсівська машина. Проти рою армівських плат. І різних ТВ-коробків. А міпс, шо, ровтери всякі - худувато і малувато. Але не будемо про сумне, бо ще трохи і я знову згадаю бідний закинутий Ітаніум, Зображення все ж ми про інше, про позітівчік так би мовити. Зображення
UEFI на сьомий арм майже ніхто не пише. На міпс точно ніхто не пише. І навіть на восьмий арм пишуть лише на хай ендові (і покишо лише майже не матеріалізовані) арм-сервери, а от на "малі" ядра як Cortex-A53 з цього океану китайських ТВ-коробків, - ніхто. Саме того вони й були вибрані як цільові архітектури. Ну все складніше - сама ідея писати UEFI і писати саме на них, виникла як поєднання різних факторів, і фактор мотивації ("нашо туди писати, якшо там уже є таке", наприклад x86), шо от нема такого на цьому діапазоні - грав свою важливу роль. Того для UEFI були вибрані ці дві архітектури.
А під них, вибирались цільові машини. На яких це все і робитиметься. І які складають парк. І які я поназивав, шоб легко посилатися на конкретну машину.
Отже, прийшов час перерахувати мої машини.
Для ARMv7 UP і MP я маю 2 машини:
  • муран - Beagle Bone Black, SoC - TI Sitara AM3358. (aarch32 - armv7 - Cortex-A8, x1, 1GHz, RAM 512MB DDR3(L)?)
  • аргон - Cubieboard 2, SoC - Allwinner A20, (aarch32 - armv7 - Cortex-A7, x2, 1GHz, RAM 1GB DDR3)
Для MIPS, як я вже казав, я маю одну машину:
  • йод - Mips Creator CI20, SoC - Ingenic JZ4780, (mips32 - mips32r2 - Xburst, x2, 1.2Ghz, RAM 1GB)
Ну і для восьмого ARM'а, я маю дві машини:
  • сосна, смерека - Pine64+, SoC - Allwinner A64, (aarch64 - armv8 - Cortex-A53, x4, ~1.1 GHz, RAM 2GB <--- !!1111 Зображення)
  • чортик - китайський Set Top Box з відроїдом інсайд, модельне ім'я - CSA90, фірма - No name, SoC - Rockchip RK3368, (aarch64 - armv8 - Cortex-A53, x8 (4+4), LITTLE.LITTLE, 1.2GHz, RAM 1GB)
Ото й увесь мій парк. Зображення Якшо зустрічатимете ці імена виділені нахиленим товстим шрифтом (або невиділені), можете посилатися на цю таблицю шоб було ясно про шо мова.
Ну і як бачите, ми почали з мурана. І не просто так - це "найпростіша" машина для почину. З багатьох причин. Основні - її компоненти (SoC головно) добре задокументовані, і вона - однопроцесорна. Бо хоч ФВ, як я вже казав, - система однопроцесорна по суті, всеодно, з наявністю більше, ніж 1-го процесору, життя ускладнюється і для ФВ. Вся решта машин - багатопроцесорні. аргон і йод - двоядерні, сосна - чотириядерна, а чортик - просто монстр - восьмиядерний, двохкластерний. Зображення Взагалі, ненарочита цей список вийшов в порядку зростання потужности машин. За пам'яттю звичайно сосна викурює всіх, але чортик має 8 ядер! Крутіше з точки зору багатопроцесорної розробки і швидкодії був би Odroid-XU4, теж двохкластерний, але вже з труЬ HMP можливостями big.LITTLE конфігурації, там 4x Cortex-A7 і 4x Cortex-A15 останні на 2 GHz, це для арма дуже багато. Чип гнумасівський, Exynos 5422 якшо пам'ятаю правильно. Але це діло стоє майже 100 умовно американських доларів. Шо найобідніше сама плата стоє мало (~76$, це найдешевший крутий ОПК), але довезення вони вибрали дороге. Мені пропонували його як подарунок. Американець українського походження. Він має свій спеціалізований магазин, який торгує такими речами, по всій Північній Америці. Пропонував просто по доброті душевній, і того, шо їхав в Україну. От такі от люди. Дякую йому. Я відмовився звичайно. Якби сам вендор запропонував, я б узяв. Зображення Вони принаймні зацікавлені в разі успіху моєї роботи. А тут просто було б занадто нахабно з мого боку приймати цей дарунок. Так, це я вже не за парк балакаю. Зображення За парк я вже наче розказав. Зображення
  • 0

#18 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 18.10.2016 – 02:13

Отже, через вельми виснажливу застуду, роботи "трохи" сповільнились, але, все ж, додам сюди іще одну розповідь уточнення.
В прикладі коду зверху, де ми шукаємо Pei.exe файл в ФТ, ми, роблячи чимало перевірок, все ж, НЕ робимо таку перевірку як перевірка гожости файла за полем State в його файловому заголовку. Типу "хвате вже цих удадських перевірок!". Зображення Але, насправді, був такий момент, коли подумавши, ми додавали пов'язані з тим полем перевірки. А потім знову прибрали їх звідти. Зображення Тож, в результаті, наче як нізашо розказувати. Але мабуть таки варто трохи пояснити про саму причину цих хитань.

Власне те поле стану забезпечує механізм відслідкувати цілісність файлу. Але це йде в сув'язі до такої речі, як внутрішнє оновлення - це коли ФВ сама себе оновлює, підчас свого виконання. От, на випадок, коли підчас цієї дуже відповідальної операції, "вимкнуть свєт", в прямому сенсі - а це цілком імовірна подія на ОПК без батарейок всяких і джерел сталого живлення, і вона, подія ця, очевидно може зацеглити вашу плату - був розроблений механізм захисту з використанням поля стану. Це неабияк ускладнює самі файлові процедури на томі. Але дає можливість побачити обвал оновлення, і відкотити все до якогось гожого стану, і навіть - успішно доробити обірваний процес оновлення.

Механізм там зробений такий. Поле стану це 8 бітів флажків. Шість послідовних знизу бітів уже визначені. Старіший біт-флажок має вищий пріритет за всі менші. Тобто сенс виставленого старшого біту перетирає сенс усіх менших. Кожен цей флажок відображає певний стан існування файлу. Коли файл створюється і це робиться з дотриманням цього механізму, флажки послідовно виставляються. І так на всьому існування файлу. Такі флажки визначені:

#define EFI_FILE_HEADER_CONSTRUCTION	0x01
#define EFI_FILE_HEADER_VALID		0x02
#define EFI_FILE_DATA_VALID		0x04
#define EFI_FILE_MARKED_FOR_UPDATE	0x08
#define EFI_FILE_DELETED		0x10
#define EFI_FILE_HEADER_INVALID		0x20

Створємо файл з "пустого" місця. Кожен біт пустого місця (і поле стану в заголовку теж) має мати значення, яке визначається параметром ERASE_POLARITY, тобто - шо логічно означає кожен біт після операції ERASE на цьому блоці. В прикладах беруть т.зв. позитивну логіку, тобто еразе полярність є 0. Тобто всі біти пустого місця мають бути нулями. Флажки тобто не виставлені. Спочатку резервуємо місце для файлового заголовку виставляючи флажок EFI_FILE_HEADER_CONSTRUCTION. Далі пишемо заголовок, і коли завершили це, ставимо флажок EFI_FILE_HEADER_VALID. Далі пишемо дані, і завершаємо це виставляючи флажок EFI_FILE_DATA_VALID. От це створений і гожий файл. Всі оновлення флажків мають бути атомарними операціями.
Коли файл видаляється, все просто - просто виставляється флажок EFI_FILE_DELETED.
А от коли оновлється, все складніше. Наявний файл помічається поміченим на оновлення, виставлянням флажка EFI_FILE_MARKED_FOR_UPDATE, потім іде створення нового файлу дубліката деінде, за вже описаною вище схемою створення файла. Файли в ФТ мають за імена GUIDи. Щойно в новому файлі дійшли до стану коли флажок EFI_FILE_DATA_VALID виставлений, старий файл перестає вважатися гожим. І лише з цього моменту. До цього моменту, саме він вважається гожим - в кожен момент часу, гожим є тільки один файл з пари. Далі, нарешті, старий файл видаляється виставлянням флажка EFI_FILE_DELTED.

Класна логіка. Явно зрозуміло для чого вона - забезпечити стійкість до помилок оновлення ФВ. Але також, видно, насікльки вона ускладнює життя двигуну самої файлової системи такого тому.
Ускладнення ідуть одне за одним, важко й перелічити. Але от пару для прикладу. В ФТ оці всі заголовки файлів, які фізично лежать перед дальшим вмістом файлу (тобто заголовками секцій і самими файловими даними), можна трактувати як розподілену таблицю директорії (папки, єдиної, кореневої, implied так би мовити) в якій містяться усі файли, і от записи в цій таблиці як і в кожній таблиці директорії - це за її вміст, про файли, але тут вони ідуть не як одна фізична таблиця, а розкидані по тому, і знаходження наступного файловго заголовку (тобто наступного запису нашої розподіленої таблиці) залежить від розміру попереднього файлу, тобто - від валідности його заголовку залежить. Якшо цього останнього нема, - розпрягайте хлопці коней.
Ну звичайно, це ускладнення не зовсім походить від введення описуваного механізму, просто є поле і для такого стану, а от чого воно таке - це вже інше питання. Бачите, в описаних процедурах, виставляння його ніде не фіґурує. Тож ми можемо вважати цей стан відображенням просто зруйнованого вмісту тому, невідновлювана помилка. Через шось.

А от уся решта горішніх бітів, як бачите, це для дальших пошуків. У видаленого файлу, заголовок гожий в сенсі знаходження наступного заголовку, тобто валятиметься це все мертвим капіталом, бо як іншаке до наступного файлу доступитися. Використати заново це місце буде ох як непросто з цим механізмом. Але можливо. Ось вам ускладнення - оновлювати файл "на місці", заразом затираючи вміст старого, не вийде - ви маєте тримати старий файл так довго гожим, поки новий не набуде стану гожости його даних, затверджений виставленням відповідного біту. Питання чи воно того варте лише для мання можливости внутрішнього оновлення? Яке власне не має ставати самометою - оновлення є метою, його можна досягти й інакше. Я казав за це щойно.

Далі іде флаг помічености на оновлення. Оце воно й є - навколо чого все обертається. Такий файл може бути гожим і його гожість залежить від флага гожости даних у дубліката. Я казав за це. Якшо оновлення завалилось, помічений на оновлення файл міг бути не видалений, але той інший новий міг бути або вже нормально готовим, або ні. Якшо у нього є флажок гожости даних, значить перше, якшо нема - друге. Тобто ми маємо тут колізії. Неважко здогадатися як це все міняє на рівні файлових операцій. Тепер наприклад треба завсіди обходити увесь том навіть якшо ти найшов свій файл, і навіть якшо він НЕ помічений на оновлення, і його флажок гожости даних виставлений. Бо всеодно є імовірність, шо оновлення таки впало і там десь ще валяється дублікат, який ще треба видалити правильно. Але про це можна лише просигналити на фазах як SEC чи PEI. І ці ускладнення впливають на кожну файлову операцію... Не вигідно.

Але був період коли подумалось, шо воно варте того. І ми стали міняти код обходу тому в Sec, все ускладнилось, але некритично, але думки про його потрібність тут все муляли. І от цей код був прибраний. Чого? Того шо, оце самооновлення справді може робити лише на фазі, яка вміє писати на пристрій постійного сховища, на наш FSD. Ані Sec, ані Pei цього не вміють - периферія ще не ініціалізована. Так, ром-код вже якось прочитав нас з SD картки чи eMMC модуля, але ми не маємо доступу до його функціоналу, тож то - до сраки двері. Треба реалізовувати повне писання в FSD самим. Яка ж це тоді "рання" фаза ініціалізації? Тим паче, нам би довелось робити набагато складнішу логіку, ніж ром коду, - імплементувати прямо тут цілий FV драйвер. Інакше, перевіряючи всі ті флажки поля стану, ми б, виявивши помилку оновлення, могли б лише сигналити дільшій фазі - Dxe, шо треба робити відновлення. І так кожен файл. І було вирішено відмовитися від внутрішнього оновлення для BFV. Тобто він у нас не може бути внутрішньо оновлений. Лише зовнішньо - за допомогою сторонньої утиліти. Власне про те, шо Pei фаза може бути non-updateable, тобто не оновлюваною (тобто взагалі), специфікація так шось тихенько казала десь в своїх глибинах. Просто це внутрішнє оновлення іде доволі дорогою ціною - значно ускладнюється файлові операції. А подія помилки оновлення хоч і можлива, але всеодно дуже рідкісна - це коли Петро захоче "перепрошити" свою плату, і тут, підчас цього інтимного (а також - чутливого, полохливого і вразливого) заняття, йому вимкнуть світло... Ну таке, не дуже часте явище. А от механізм захисту від цього рідкісного явища має дуже відчутний влив на кожну файловосистену операцію в ФВ, і впливає це на всіх, починаючи з Sec фази. Не круто. Але головне - оновлення і без цього всеодно можливе - просто зовнішнє. І це лише про BFV, CFV ми ще не рішили, але там принаймні справді бігатиме драйвер ФТ, і драйвери відповідних пристроїв сховищ (SD, eMMC, ще шось). і вони умітимуть разом писати на диск. Тож є хоч сенс робити ті перевірки, бо є можливість реально імплементувати цю логіку до краю.

Зовнішнє оновлення - значно простіше. Для SD карток (це переважно розробницька стадія для нас, SD картки не найкраще місце для ФВ, я казав за це, для розробки це дуже зручно, для звичайного використання, треба переповзати на якесь менш ремовабле сховище, але суть плати, які не мають такого, в моєму парку - це сосна наприклад, тож SD картки - єдина опція там і значить - це не лише розробницька опція, але й "випускна" також), так от, для SD карток, зовнішнє оновлення - це просто супер просто - ти за допомогою утиліти пишеш в ФТ на SD картці, все, шо тобі треба. Сидячи за своєю робочою станцією. А от для постійних варіантів, не висувних так би мовити - наприклад eMMC модулів, тут складніше, але знову - принаймні можна піти шляхом флашерів - ти маєш спеціальну ФВ на SD картці, яка пише на eMMC. З т.з фірмварі, вона сидить на SD картці як своєму постійному сховищі, і нічого там не оновлює, а просто кудись нашось шось пише. Тож якшо свєт вимкнуть тут, ця оновлювальна ФВ не стане пошкоджена, і знову почне писати eMMC, як свєт ввімкнуть. Поки не зробе це успішно. Потім пользун наступного разу вантажитиметься з новенької ФВ на eMMC (не забувши переключити режим вантаження, специфічний для плати і, звичайно, - витягти SD картку з ФВ флашером!) Розумієте тепер, чого це - зовнішнє оновлення? Бо той хто оновлює, НЕ оновлює себе, свої файли. Навіть якшо це флашер, зроблений з тієї самої ФВ. Та сама за класом, але не за зразком. Ще ясніше, якшо ми "зразок" прив'яжемо до його постійного сховища. Зовнішнє оновлення - це коли ФВ (флашер) або інший аґент (хостова утиліта) оновлює вміст постійного сховища для якогось зразка ФВ, але не своє постійне сховище, де це релевантно (флашер). Це суть.
Так от, для BFV саме так (зрештою, трохи не так, читайте до краю Зображення хто хоче знати). Це питання балансу. Так вигідніше. Тепер на всіх фазах, які працюють з BFV і лежать там, а це SEC PEI сув'язь, файлові операції простіші.
А як же цілісніть, гожість? Ну я ж кажу, подивіться код, ми вставили майже десяток перевірок різних полів, пов'язаних як з семантикою ФТ так і PE файлів. Більшість цих полів 4-байтна. В разі пошкодження даних тому, імовірність, шо випадково всі поля виявляться з потрібними значеннями, але дані всеодно не гожими, майже нульова. І ці поля не лежать один біля одного між іншим, але лежать коло даних, тож їхня валідність - це напвено і валідність даних.
З точки зору цих фаз і механізму ВО (внутрішього оновлення), вони (фази) розглядають (чи вимагають) том, який не має містити колізій. Ніяких і спричинених помилкою ВО також.

Допитливий читач може поцікавитись, а чи не міг би Dxe всеодно старатися оновлювати і BFV? Добре питання. Міг би в принципі. Але якшо всетаки помилка ВО станеться і вона приведе саме BFV в неконсистентний стан, то ранні фази не помітять цього, і теоретично можуть робити неправильно або не робити зовсім. Саме - якшо наприклад свєт вимкнуть на оновленні файла Pei.exe - Pei Core файла, так, шо оновлений файл виявиться за старим, Sec фаза всеодно завантажить (запустить) старий. Це може бути і не страшно, але певна непевність є. Потім, коли Dxe отримає керування, він просканує ФТ, і, помітивши некосистентність, виправить її. Якшо можливість оновлювати внутрішньо BFV варта того, згадану непевність можна терпіти. Думаю, відповідь - скоріше так, ніж ні. Треба розглянути трохи глибше, чи нема там якихсь неприємніших наслідків. І якшо нема, то ми введемо ВО для BFV, за яке SEC PEI будуть не аваре так би мовити. Зображення Це буде такий собі гібридний варіант - ВО для BFV є, але Sec і Pei фази нехтують ним і пов'язаним з ним гемороєм. Оскільки лише Dxe справді може це все робити, - лише вона й буде аваре за це. Це вже якшо зовсім точно описувати наш варіант.
Такий вибір виглядає логічним, таким, шо відображає природу і можливості фаз, про поведінку яких ми рішали.

Підсумовуючи, можна сказати, шо спочатку ми якось туманно знехтували враховуванням цього механзіму. Це не так як треба робити. Тепер ми обдумали і знаємо й розуміємо чого ми відкинули для ранніх фаз цей механізм.
Нижче наведемо нову редакцію нашого файлу Sec.S. В ній були додані а потім прибрані обговорювані перевірки, Зображення а також в ній уже є розбирання PE полів і ми вже дійсно "склеїли" файлове розбирання з передаванням керування на PeiEntryPoint(), тобто написали PE частину. Але вона значно спростилася, як я й описував в дописі про XIP. Тепер, коли наш mmbfv є XIP сховищем, завантаження PE файлу - це всього пару перевірок. Які можна було б уже й не робити оскільки купа перевірок вже зроблена, ну але, якось стрьомно, а вдрух ми покладемо в ФТ на місце Pei.exe не Pei.exe, а файл з Гаврилівною?!1 Зображення От на цей випадок додали ще PE перевірок. Єдине, шо там справді треба - це зчитати поля Опціонального Заголовку PE32 файлу, - AddressOfEntryPoint і ImageBase, і склавши їх (арифметично), передати туди керування, тобто на ARMі - написати ту суму в pc (r15). Є в нас і таке. Зображення
Шодо питання, "а як же кеш підтримка, пов'язана з XIP"? Це буде, якшо воно треба, але оскільки, це ніяк не пов'язана з обходом тому операція, і навпаки, вона може бути зроблена з самого початку, бо насправді ми ж нічого не міняємо в mmbfv (крім флажку відображености в пам'ять), то це буде доречно робити саме в блоці який возитиметься з вмиканням I-кешу і ВГ. Але суть в тому, шо все більше виясняється, шо тут нічого власне налаштовувати пов'язаного з цим. Зображення Власне нема чого інвалідизовувати кеш. Чи чистити його. Ніякого самозмінного коду ми тут не маємо. Ми погарячились думаючи, шо тут треба шось робити. Зображення Бува й таке. Насправді просто толком не знали. Тепер стає видніше, але ще треба всетаки уточнити, всеодно це буде в блоці I-кешу і ВГ, бо там йому місце. В коді це іде майже найпершим. Ітиме. Зображення Але мінімум - ми просто ввімкнемо I-кеш і ВГ, записавши в регістр контрольного копроцесора P15. В наявному коді, це там, де ми вимикаємо MMU. Плюс, можливо треба буде злити конвеєр процесора, виконавши "бар'єрну" інструкцію ISB. Оце ж і є "уточнити". Неважко вставити 1 інструкцію, навіть таку, яка зливає весь конвеєр і всі проміжні результати виконання інструкцій, вона всеодно всього одна, а які в нас там інструкції, ми ж тільки почали, але важливо розуміти чи вона там треба і чого. Це і є розуміння суті. З ним набагато легше, але воно дається трудом. Трудом, а не писанням банальностей! Зображення Отже потчоний код SEC.S: Зображення
Прихований текст

  • 0

#19 taraBooka

    барабука)

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1459 повідомлень
  • Стать:Жінка

Відправлено 18.10.2016 – 13:23

Перегляд дописуKassandra (23.09.2016 – 11:17) писав:

[ex]флейм почистила[/ex]
Касю, ти мене вразила :) - ти це все прочитала!!
  • 0

#20 _Ex

    STATUS_OK

  • Користувачі
  • PipPipPipPipPipPipPipPipPipPip
  • 1413 повідомлень
  • Стать:Чоловік
  • Місто:Бахмут, Південна Слобожанщина, Україна

Відправлено 18.10.2016 – 22:44

Перегляд дописуtaraBooka (18.10.2016 – 13:23) писав:

Касю, ти мене вразила Зображення - ти це все прочитала!!
зачиталася. Зображення Насправді у неї професійно око набите так би мовити виявляти флейм серед флуду. Роки модерування на теревенях, не проп'єш. Зображення

До речі, про ускладнення файловосистемних операцій на томах з розгорнутим механізмом ВО. На відміну від ранніх фаз, де не було б іншого варіанту як робити "парціальне" сканування, на кожному файлі, на предмет помилки оновлення, - ну бо просто нема в нас на ранніх етапах всього двигуна ФС, на етапі, який вміє робити і писання на диски і імплементує двигун ФС (тобто на Dxe фазі), виявилося, не треба буде кожну файлову операцію робити оглядаючись перелякано на можливі колізії і решту ускладнень. Просто треба на тих кодових шляхах завантаження, на яких взагалі можливе падіння оновлення (холодний ресет наприклад, а от який небудь сонний режим ну ніяк не може йти в купі з падінням оновлення, самі подумайте чого), треба робити сканування томів (тобто робити перевірку в добревизначеній точці). Це очевидно треба робити в рамках ініціалізації фази. А потім спокійно працювати на томах. Ясно, шо для ФТ, які не дуже писабельні за природою, і в нормі - переважно лише читабельні, будь яке писання є оновленням. Але успішне оновлення не вимагає ніякого сканування, отже треба сканувати лише на ініціалізації. Це добре. Зображення
Як воно виглядатиме? Звичайно за це ще рано балакати, це коли писатимемо драйвер FV для Dxe це буде актуально, але оскільки ми зараз в цьому, - перші нариси.
В рамках сканування томів, код обходить кожен том і пофайлово перевіряє флажки стану в файлових заголовках. Якшо флажок EFI_FILE_HEADER_INVALID виставлений - розпрягайте хлопці коней. Зображення Я навіть не знаю як таке може бути. В принципові, ми можемо чесно падати тут. Але можемо також розгорнути якусь евристичну фігню. Головна ідея, - це має бути конфіґурованою річчю. Пользун має мати вплив на це. Падати чи робити евристику. Якшо друге, то наприклад, ми можемо спробувати таки знайти наступний заголовок читаючи поля цього. Якшо вони справді негожі, вони дадуть якісь липові розміри, і там ми знайдемо якісь довільні дані, буде таки видно - заголовок справді не гожий, падаємо. А якшо полізши за адресами, обрахованими по зчитаних розмірах в нашому заголовку, ми побачимо якісь очікувані дані на певних місцях, з'являється шанс, шо це мабуть таки гожий файл попри бредовий флажко в заголовку, є надія. Шо це за очікувані значення? Ну от наприклад, поле типу файлу - існує кілька типів, визначених, наші файли точно якихсь з них, тож попадання тут в один з таких типів - добра ознака. Далі, є ще GUIDи імена, теж визначені, знаходячи саме їх а не шо попало, це вже огого, точно не збіг. Ну і поле стану наступних заголовків. Шо там має бути в нормі? Очевидно EFI_FILE_DATA_VALID і нижчі біти. Ось якшо й цей патерн буде, - то точно все значно краще з томом, ніж здавалося. Тоді треба рішити, шо робити з попорченим заголовком. Мінімум, просто зняти той брехливий флажок в ньому, але ще треба мабуть перевірити чи не виникає колізій пілся цього. Взагалі цей флажок - це повна фігня, цього просто не має ставатися. Зображення
Далі, ми дивимося чи не виставлений флажок EFI_FILE_DELETED. Якшо так, пропускаємо цей файл і вираховуючи наступний заголовок, ідемо далі.
А далі, дивимося чи файл не має виставленим флажок EFI_FILE_MARKED_FOR_UPDATE. От якшо має його виставленим, оце воно. В нормі то цей флажок мав би пробути виставленим зовсім недовго - поки новий файл не набуде стану гожости його даних, потім процедура оновлення, вертається до старого файла і ставе в ньому флажок видалености. Бачите, якшо флажок видалености виставлений, то флажок позначености на оновлення, і решту нижчих флажків ми іґноруємо - це те, за шо я казав, старші флажки перетирають сенс молодших. Зображення Якшо флажок оновлення так і залишився найстаршим виставленим до часу сканування (на наступному холодному ресеті, вмиканні тощо), значить "виключили свєт", виникла помилка оновлення. Тепер треба шукати дублікат, і дивитися його стан. Якшо там флажок гожости даних виставлений, той файл "перемагає", і процедура сканування, просто завершує обірване оновлення, ставлячи на старому файлі флажок видалености. Якшо на новому файлі флажок гожости даних не виставлено, це скаладніше. З одного боку валідним є старий файл, і ми можемо просто зняти на ньому флажок оновлюваности, а на новому поставити флажок видалености (правда лише за умови шо він принаймні має стан гожости заголовку - лише тоді ми можемо крокувати за нього, нам треба знати його розмір, який він хотів собі узяти). З іншого боку, треба ж якось доробити те оновлення. І тут не дуже видно, бо не уже видно інтерфейс з користувачем. Протокол оновлення. От якось треба перезапустити його. Тобто знайти джерело оновлення (файли, які треба писати, і опис, які саме файли вони оновлюють, адже не завсіди файл X перетирає файл X, може бути, шо файли X, Y оновлюються, файл Z з'являється, а файли U, V зникають внаслідок оновлення). І тут ще не ясно. Як добре, шо це аж на Dxe фазі! Зображення Але всеодно, оновлювати ми маємо на консистентному томі, тож всетаки треба привести до безколізійного стану, знявши марковані флажки на файлах, в яких негожі нові напарники, і поставивши флажки видалености на старих файлах в яких нові напарники гожі. Тут до речі сплила одна неприємність - якшо свєт виключать, коли файл буде в стані EFI_FILE_HEADER_CONSTRUCTION, тобто коли заголовок не має валідних полів розміру, - це явно халепа, том зіпсуто остаточно. Розпрягайте хлопці коней. Зображення Знову можемо, залежно від конфіґурації, або одразу падати, або робити евристику спочатку, надіючись, шо поле розміру в цього заголовку все ж гоже, і йдучи підтверджувати це чи спростовувати, читаючи наступний заголовок, вже потім падати, або таки продовжувати, піднімачи стан файла до EFI_FILE_HEADER_VALID. Дуже непевно це тут. Але принйамні ця ситуація - ультрарідкісна, треба, шоб свєт вимкнули якраз тоді коли процедура оновлення "забиватиме" місце для нового файла, і помітить стан як стан будування, але не встигне написати правильні значення полів розміру, імени і решти, і виставити стан гожости заголовку, бо якраз тут вимкнуть живлення. Я оце думаю - а на якого дідька вони взагалі ввели цей стан - будування? Адже можна було б спочатку писати в вільне місце гожі поля, і потім, в кінці ставити EFI_FILE_HEADER_VALID. І було б або вільне місце або гожий заголовок, навіть на падінні в цій точці. Ніяких ахтунгів негожого заголовку. Якшо так можливо робити - ми саме так і робитимемо, тоді в нас сценарій, де лише флажок конструкції буде виставлений, буде просто нереальний, і ми ніколи не попадатимемо в описану вище неприємну ситуацію, і як зараз виглядає - штучну!
А от шодо кількостей - як багато може бути колізій і прочих фігонь, то явно, хоч ми балакаємо за "багато" файлів, очевидно, на помилці оновлення, майже ґарантовано, така пара буде лише одна. Бо цей процес НЕ розпаралелений. Він послідовний за своєю природою. І ФВ, пам'ятайте - однопроцесорна, однопотокова сутність. Тож якшо й приведемо в неконсистентний стан, то лише на якомусь одному файлі. Це добре!
Якшо нема більше файлів помічених на оновлення, все, сканування завершено, ідемо сканувати наступний том. Можна зробити жорсткішу вимогу, і перевіряти, шоб також усі файли принаймні мали флажок EFI_FILE_HEADER_VALID виставленим, бо лише так ми справді можемо робити обхід тому, лише такий том є валідним. Це просто потреба, нам неможна іґнорувати негожі заголовки, ми всеодно на них прийдемо богзна куди. Тоді, якшо заголовки всі гожі, все ОК, якшо ні, ситуація потворюється з явно виставленим флажком негожости заголовка, дивись вище, тобто або падаємо, або евристика і виправлення заголовків в разі успіху останньої. Падіння в разу неуспіху.
Оце такі нариси на далеке майбутнє писання Dxe драйвера FV. Зображення
  • 0



Кількість користувачів, що читають цю тему: 1

0 користувачів, 1 гостей, 0 анонімних