пʼятниця, 19 серпня 2016 р.

Структура сховища і потік виконання перших фаз. перші спроби й обриси.

В продовження попереднього допису, опишемо трохи фізичну конструкцію нашого стартового коду, як він планується бути оформлений і яка послідовність виконання. Це все дуже поверхнево і неостаточно і це саме для BBB.
Вивчаючи інтерфейс ром коду на Sitar'і, як і слід було очікувати, виникли суперечності між документацією і реальною поведінкою. А також вияснилося, шо принаймні в частині опису ініціалізації, цей 5000-но сторінковий TRM - це каша каша каша. Шо ж, довелося таки питати в мейлинг-листах, я наче вже писав за це, шо прийдеться, ну а там мене перенаправили на питайте-можливо-відповімо сайт самого TI. Ну от чекаю тепер. Просто як нотатка, прикол такий - ситарин ром-код каже, шо на вбудованій пам'яті, тобто eMMC в нашому випадку, а також може бути NAND, ну і як зовсім екзотичний і мною ніколи не бачений варіант - eSD, він не робитиме MBR/FAT завантаження, а робитиме сирий режим - просто читатиме перший сектор, оцінюватиме чи є там образ і вантажитиме той образ прямо, і прямо на нього передаватиме керування. На картках же, він ще може робити MBR/FAT. Виявилось, шо на мурановому eMMC, нема нічого крім нулів в першій половині MBR (а саме - в BootCode блоку), де тільки й міг би лежати той голий образ для сирого режиму. Зато є файл MLO в корені FAT16 партиції, шо натякає, шо ром код на мурані таки робе файловосистемне завантаження. Точно навпаки як написано в документації. Круто!
Гаразд. Думаннями, ми надумали нарешті, як виглядатиме наш пристрій сховища структурно. Розкрій так би мовити. Той, шо англійською - layout. Розкрій мені подобається. Це первинно так. Далі уточнюватимемо (згадки за певні уточнення сюди додані, оскільки цей блог дає можливість редаґувати статтю).
Отже спочатку словесно, а потім мабуть ще картинку потворну намалюємо для іллюстрації. Розмір сектора 512 байтів тут.
Захисна MBR (Protective MBR)
Розміром в один сектор. Ця структура служить для зворотньої сумісности і для захисту GPT структур і партицій від нетямущих старих утиліт. Вона описує увесь наявний простір за собою (включно з рештою GPT метаданих) як одну неактивну партицію. Оті перші 440 байтів MBR, BootCode блок не використовуються UEFI, але нашою Sec фазою таки будуть використовуватися (не будуть, дивись уточнення нижче), по-перше цьому нема альтернативи, а по-друге, тут, на армівських і міпсівських ОПК, це не створить ніяких проблем (сумісности), того а чого власне не використаовувати цей простір? Ром код не розуміє GPT партиціонування, того ми можемо лише використовувати сирий режим. А в ньому, ром код диктує, де і як має лежати навантаження. І це майже завсіди - найперший сектор (а також якісь наперед визначені дуплікативні місця, як наприклад сектор #256, #512, #768 у випадку з ситариним ром кодом, але це майже ніким і ніколи не використовується (додане пізніше: так, майже "ніким", яка іронія, саме ці місця і доведеться використовувати див. нижче)). Тобто очевидно, ром код очікує GP заголовок (а це два 32-бітні поля для розміру і адреси куди вантажити), і сам образ в секторі #0 і далі (важливе уточнення: в секторі #0 він очікує лише маркер сирого режиму - CH, а сам образ - очікує в наступному секторі). Ми умисно оминаємо проблему з CH, бо як я писав вище - треба почекати на відповідь, але суть шанси, шо він не потрібен, і от з таким оптимістичним варіантом ми і йдемо - у нас GP заголовок і сам образ1. Така схема не конфліктує з PMBR і GPTH - вони маленькі і можуть бути якби включені в Sec шматок. Вони будуть завантажені, вони нам всеодно потрібні, все нормально. Навіть на міпсі криейторі (йод), такі об'єми влізуть (там 14КБ макс. розмір навантаження). Отже наша Sec фаза (в нашій термінології це Sec1) фізично розбивається на дві частини - Sec1.0 та, шо лежить в BootCode блоку, і потім перестрибує через решту PMBR і GPTH на Sec1.1 - решту, шо не влізло в BootCode блок. Цю другу частину я вирішив поки шо зробити 2 сектори в розмірі - 1024 байти. Тобто первинно ми вибрали Sec1 фазу в розмірі максимум 1456 байтів:
(1024 (Sec1.1) + 440 (BootCode) - 8 (GPH)). Це 364 армівські (і міпсівські теж) інструкції. Не мало, якшо не роздувати чортівні.
GPT Заголовок (GPTH)
Ну заголовок як заголовок. Все як написано в специфікації на GPT. Не переказуватиму. Він має фіксовану позицію - сектор #1, і розмір в секторах - один сектор, тобто 512 байтів в нашому випадку.
Sec1.1+BFV
Далі іде вже згадана нами друга частина Sec фази, визначена в два сектори розміром. І - BFV - Boot Firmware Volume. Фірмварний завантажувальний том. Той, де лежить Pei з Peim'ами. Ми визначили його розмір в 16КБ. Керувались тим, шо рівнятися треба на той випадок, який обмежує в розмірах. Шоб ця фаза була однакового набору функціональности на всіх наших машинах. А обмежує в розмірах нас міпс криейтор. В якого максимум навантаження - 14КБ. У мурана 109КБ. А в аргона точно не знаю, але виглядає, шо десь посередині - шось типу як 48КБ орієнтир. Також з метою ефективности. Нам не треба розганяти апетити. В цих 16КБ буде лежати код, який зрештою ініціалізує центральну логіку і пам'ять. І запускає завантажувач решти ФВ. Як я вже казав, ми маємо два ФТ - BFV і CFV. Не пам'ятаю чи казав я за облік їх в GPT, наче казав. Так от. Я якось спочатку узівав, шо можна ж якісь обліковувати, а якісь ні. Спершу я думав, за принципом - або обліковувати всі або ніякі. Але потім стало ясно, шо нам лише BFV треба покласти найближче до початку, тобто перед масивом партицій, який має йти перед будь якою облікованою ним партицією і має бути шонайменше 16КБ в розмірі. Ясно, шо ми не можемо його вантажити в SRAM - не влізе. Значить BFV не попадає в GPT облікування, але CFV нічого не заважає туди попасти. Отже так і буде - CFV йтиме як перша партиція в GPT. А вертаючись до нашої сув'язи SEC+BFV, ми маємо 17КБ в розмірі.
Масив партицій GPT (GPTPA)
Знову як і заголовком - все як у стандарті. Ми вибираємо йому 16КБ того мінімуму, який вимагається. Бо цей мінімум і так величезний для сховищ нашого діапазону. Справді, маючи, шо кожен запис в цій таблиці бере 128 байтів (може більше, але ми не робитимемо цього, це просто не треба), ми маємо потенційно 128 партицій! Першою карткою я обрав 16GB-ну, - це більш, ніж досить для неї. Насправді 128 партицій - це більш, ніж досить для будь якого нині сущого пристрою сховища.
А які ж партиції я вирішив створити?
CFV
Я писав уже, шо це таке. Це власне вся фірмваря крім, BFV і системної партиції з ОС лодарями і картинками. Тут таке - чим далі в ліс, тим гладкіші партизани. менше визначености тобто. Який розмір? Я вибрав 128КБ. Думаю цього має вистачити. Ми хочемо робити ефективно. Без отого роздування. Для коду всієї фірмварі, цього має вистачити.
Системна партиція FAT тип
Далі іде архітектурна штука - системна партиція. Куди кластимуться певні речі від фірмварі і головно, - куди сторонні розробники драйверів і лодарів кластимуть свої поробки. Не знаю який саме FAT вибрати. Але напевно або FAT16 або FAT32. Розмір. А хто його зна? Якшо тут кластимуть якісь бітові карти для графічного інтерфейса і логотипів, то це вже мегабайти. Тож це ще треба уточнювати.
А далі ідуть просто користувацькі партиції. В рамках тренування, і для цікавости, я планую створити кілька FAT партицій, шоб же подивитися, як Віндовс (та й решта) сприйме SD картку з наприклад п'ятьма партиціями і GPT схемою! Це до біса цікаво. Але треба вже зараз вивчити FAT будову. Воно того варте.
Звичайно, в кінці диску, мають іти дзеркальні копії GPTH GPTPA - але знову, - це стандратно. Треба ще навчитися запитувати точний розмір пристрою, шоб не губити жодного сектора, а поки я не знаю як це робити, ми кладемо логічну 2-х гігабайтну картку в фізичну 16-ти гігабайтну. Взнаємо як читати розмір картки, розширимо схему, і пересунемо дзеркальні структури в справжній край. (додано пізніше: я вже знаю як взнавати розмір пристрою, дякуючи простій і ясній кодовій базі відомої утиліти Win32DiskImager, і всемогутньому MSDN'у. Ключова функція в цій справі, - DeviceIoControl(), ну й іо контроли звичайно).
Далі ще треба покласти ілюстрацію вищеописаного (ілюстрація нижче).
А в кінці, - невеликий підсумок кодової послідовности на цьому етапі. Отже ром код вантажить нашу sec1 фазу. Вона в SRAM'і має відображений BFV (mmbfv), вона налаштовує потрібні структури, встановлює стек. Шось може налаштовує в процесорі. Наприклад, ситарин ромкод лишає вимкненими армівські предиктор галуження, кеші даних і коду, першого рівня, шо там уже казати за другий, вимкнена ясно й MMU. Але я не знаю, де я це налаштовуватиму - тут, в sec1, чи вже в Pcr.sys. А от ще там годинники (в смислі не таймери, чи ГРЧ а саме тактові сигнали, clock, всі оті APPLS і APLLLJ) можливо треба тут буде налаiтовувати. Watchdog таймер також. Ну і обробники винятків архітектурні армівські. Навіть якшо спочатку можна покластися на ром код, так же як і з тактовою логікою, це лише на перших кроках - адже багато чого не налаштовоано ром кодом як з PLL - не всі вони налаштовані і не всі так як треба саме на цьому етапі, а лише ті, які йому потрібні. По мінімуму. І з векторами - вони трівіальні, в повнофункціональній системі, фірмваря має перебирати на себе цю роль і налаштовувати ці речі так, як треба їй і подальшим фазам. Тож є шо робити. А ще скільки всього не відомо.
Потім, sec1, з mmbfv, завантажує Pei.exe (завантажує як виконуваний образ), якому як ми казали, не треба робити релокацію (в нормі, єдиний варіант відхилення від якої, який я зараз можу вигадати, - це коли сторінка за адресою бази Pei.exe може виявитися "паганою" апаратно, для цього ми лишаємо релокаційну інформацію в образі Pei.exe і маємо вміти в sec1 робити релокацію. Але це не важко в PE.). І передає на нього керування і аргументи визначені в специфікації. Адреси в SRAM'і, де Pei може розгорнутися. Адреси стека тощо. Далі Pei робитиме так само свою роботу, серед якої завантажить з mmbfv Pcr.sys, який ініціалізує процесор, чипсет і па'мять (DRAM). Це останнє - значна подія. Мало я ще уявляю, шо там за робота, але уявляю, шо її багато. Нарешті в кінці запуститься Dxe Ipl, чи то як окремий драйвер, чи як частина Pei.exe. І він робитиме, шо там специфікація йому велить. А також завантажуватиме з сховища CFV, тобто робитиме shadowing тому, роблячи його відображеним в пам'ять - породжуватиме mmcfv. Шоб усі подальші фази, звертались саме сюди, коли їм треба прочитати з фірмварного сховища. Це ж вам і кешування. Лише оновлення фірмварі писатиме в том на пристрій. А також збереження всяких змінних і конфігурацій в нелетке сховище. Після роботи цього модуля (Dxe Ipl), ми маємо мати розгорнуту повну Uefi Core, готову до виконання. Це вже інші етапи, які ще не вивчені і не пророблені зовсім. З точки зору їхньої ініціалізації і детального розуміння їхньої роботи, бо я маю навіть певні сервіси написані. :) Але це одне й друге - ще дуууже далеко одне від одного. От треба наближати їх.
Додано 24.08.2016
Круто, написав вчора сюди трохи уточнень, оскільки вже не було часу перевіряти описки, повернув допис в чернетки, приходю сьогодні доробити, додати малюнок, а редаґування як вітром здуло. Круто! Зато оновлює автоматично текст разів десять за хвилину. Обожнюю таке.
Гаразд, напишемо знову.
Я писав вище, шо спитав на форумі TI і чекаю на уточнення. Ну от мені відповіли і відповіли змістовно. І, тепер, треба вносити поправки в структуру. Але я вирішив не змінювати вже написане, а дописати, - у нас не посібник, а щоденник. Це відображає хід роботи.
Отже, на eMMC БББ, ром код таки робить MBR/FAT читання. Просто TRM каже неправду, шо він не робить такого на вбудованій пам'яті. Далі, ром код таки вимагає CH в першому секторі (на чотирьох позиціях, дивись нижче) якшо ти хочеш сирий режим. Ті структури, які він вимагає, влазять в BootCode блок, вони десь 288 байтів (так, 288 бісових байтів, шоб просигналити, шо тут є валідний образ). Але хоч той CH і влазе в BootCode блок і ще трохи місця лишає, нам це не допоможе поєднати це все з GPT, бо ром код очікує образ на початку наступного сектору, бо він, бач, хоче, шоб образ був вирівняним на сектор (нашось). Шо найцікавіше, про це ні слова в TRM'і. Дійсно, така дрібниця. Того, BootCode блок ми не можемо використовувати. Більше того, в наступному секторі у нас іде фіксована структура - GPT заголовок, і ми ніяк не можемо сумістити це з кладінням сюди образу (ромкод хоче там GP заголовок і передає керування на перше слово за заголовокм). Халепа. Але вихід є. І він полягає в тому, і це продовження нових знань отриманих з відповідей, шо ромкод для сирого режиму впевнено робить всі чотири спроби читання в секторах 0, 256, 512, 768, і лише тоді, коли не знаходить в жодному вказівки на гожий образ, іде до MBR/FAT завантаження. Це важливо, бо я не мав такої впевнености (наприклад я думав, шо хто зна, а може він не знайшовши валідного сирого образу в першому місці, але побачивши там MBR сигнатуру, стрибне в MBR/FAT режим. Дійсно, а шо його думати, коли все так паскудно задокументовано, і ще й явно поведінка розходиться з тим, шо написано). Тобто, ми можемо покласти все майже як збирались спочатку, але в сектор #256, пропустивши 256-34=222 сектори (34 сектори - це PMBR, GPTH, GPTPA). Це марнує 111КБ місця, проте дає можливість не розбивати sec1 на два шматки і також - обліковувати BFV як партицію (без необхідности завантажувати GPTPA в SRAM). Але головне - це не наша вина, це вимога ром коду. Суть і інші варіанти, але вони гірші. Наприклад ми можемо покласти в це пусте місце якусь іншу партицію, а далі BFV. Але це і небезпечно і незручно. Дані, які вантажаться на таких ранніх стадіях і які містять фірмварю, - це особливі дані, і спокійніше, коли вони лежать десь на переді, або взагалі на своєму пристрої. Мати якусь фіксовану точку, десь всередині сховища для фірмварі, між довільними даними - це не те. Або є так звана гібридна MBR - найсправжнісінькі костилі. Вона поєднує в собі захисну MBR і також дублює облік кількох GPT партицій (згадаймо, PMBR з чотирьох записів в таблиці записів, використовує лише один - для захисної партиції - якою покривається увесь простір за PMBR, решта записів мають мати нулі, але гібридна схема має там записи, шо дублюють інформацію про якісь вже обліковані GPT партиції. В цьому і є весь трюк - потім код, який не розуміє GPT (як от наш ром код), може використовувати інформацію за партиції з цих записів, для якого вони суть звичайні MBR записи). Але це взагалі ахтунг. Це добре коли такий код лише читає. Як ром код. Можна лише уявити, чим це все може закінчитись, якшо там влізе хтось модифікувати ті партиції. Ані одна ані інша сторона не поважатимуть один одного. Дві хазяйки на одній кухні детектед. І це звичайно суперечить GPT специфікації, це вже не GPT. Отже, просто зсовуємось на 256 секторів вперед. А облишене місце ми можемо використовувати для чогось ще - таке навіть GPT специфікація дозволяє. На відміну від гібридної MBR. Шо саме ми там можемо зберігати? Поки не знаю. Але от варіанти - логотипові картинки, користувацького інтерфейсу картинки. Або сховище для змінних і іншої конфігурації, яка вимагає нелеткого сховища.
Ці новини змінюють наш розкрій дещо. Як вже сказано, ми не розбиваємо sec1, і тепер вона суцільна, один бінарник, який лежатиме на LBA #257 одразу за сектором, який показує ром коду, шо тут лежить потрібний образ. Все як він хоче. Який розмір sec1 фази? Це ще не відомо, але тоді в нас було коло 1.5 КБ, тож можемо вибрати 4 сектори, закруглючи так би мовити. Це може змінитися. Але як перший варіант хай буде 4 сектори. Далі BFV в 16КБ (32 сектори), далі CFV 128КБ (256 секторів), далі системна партиція (SYSP) неясно ще в скільки КБ. Попри те, шо тут BFV і CFV лежать бік о бік і не рознесені як в першому варіанті, ми не поєднуємо їх в один том. Бо для цього суть купа причин. Головна - BFV відображається і його вміст працює в SRAM, CFV - в DRAM. Різним кодом і в різні фази, з різними можливостями. Набагато більше вигід в таких реаліях мати два томи, ніж об'єднувати їх. А додаткові дані на облік, які це розбиття забере - всього пара сотень байтів. Ну і це лише на БББ так. Наприклад на аргоні (кб2), там свої будуть причуди у ромкода (бром він у них називається), я ще мало знаю за нього, але там можливо підійде та первинна схема, з розбитою надвоє sec1 фазою, BFV поза обліком і CFV як партиція. Головне - таке розділення обумовлене архітектурою сучасних машин, коли спочатку в доступі є лише маленький SRAM, а потім значно більший DRAM. Код в BFV працює в першому, код CFV - вся фірмваря, працює в другому. Це насправді оптимізація мати це розбиття. От наприклад, уявіть, Pei ядру треба завантажити Pcr.sys. Шоб ініціалізувати ту ж динамічну пам'ять. Так вона шукатиме його в маленькому BFV томі, там всього кілька файлів, а з одним томом їй би прийшлось перевертати значно більший том з значно більшою кількістю файлів. Враховуючи просту пласку структуру фірмварних томів, без папок, з файлами які ідуть один за одним - це важливий фактор. Мати маленький том в обмеженій фазі, це вигідно. І по часі і по простору.
На хід виконання ранніх фаз ці зміни майже не вплинуть. sec1 фаза тепер нікуди не перестрибуватиме. Також не буде в SRAM'і GPTH. І нам треба його самим завантажувати, коли він знадобиться. Це ясно шо не страшно. Власне так навіть трохи чіткіше виходить. Шкода лише, шо місце марнується.
А це іллюстрація з врахуванням змін.
Додано 24.10.2016: На малюнку все правильно, крім кількох моментів. Створюючи першу картку з цим розкроєм, ми зсунули SYSP на LBA #512, таким чином CFV став трохи менше за 128 КБ (на розмір CH+SEC+BFV, тобто на 37 секторів, 18.5 КБ). Розмір SYSP теж вибрано було так, шоб юзерська партиція лежала на рівній адресі (LBA #40000h), SYSP відтак іде трохи менше за 128МБ.

Немає коментарів:

Дописати коментар