Rev 1962 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1065 | Lrz | 1 | ; Copyright (c) 2008-2009, diamond |
2 | ; All rights reserved. |
||
3 | ; |
||
4 | ; Redistribution and use in source and binary forms, with or without |
||
5 | ; modification, are permitted provided that the following conditions are met: |
||
6 | ; * Redistributions of source code must retain the above copyright |
||
7 | ; notice, this list of conditions and the following disclaimer. |
||
8 | ; * Redistributions in binary form must reproduce the above copyright |
||
9 | ; notice, this list of conditions and the following disclaimer in the |
||
10 | ; documentation and/or other materials provided with the distribution. |
||
11 | ; * Neither the name of the |
||
12 | ; names of its contributors may be used to endorse or promote products |
||
13 | ; derived from this software without specific prior written permission. |
||
14 | ; |
||
15 | ; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka |
||
16 | ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
17 | ; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
18 | ; DISCLAIMED. IN NO EVENT SHALL |
||
19 | ; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
20 | ; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
21 | ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
22 | ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
23 | ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
24 | ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
25 | ;***************************************************************************** |
||
26 | |||
3539 | clevermous | 27 | Нет повести печальнее на свете, |
28 | Чем повесть о заклинившем Reset'е... |
||
1065 | Lrz | 29 | |
3539 | clevermous | 30 | Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает |
31 | Windows, для носителей с размером сектора 512 байт. |
||
1065 | Lrz | 32 | |
33 | ===================================================================== |
||
34 | |||
3539 | clevermous | 35 | Требования для работы: |
36 | 1) Все используемые файлы должны быть читабельны. |
||
37 | 2) Минимальный процессор - 80386. |
||
38 | 3) В системе должно быть как минимум 592K свободной базовой памяти. |
||
39 | 4) Пути к используемым файлам не должны содержать символических ссылок NTFS |
||
40 | (жёсткие ссылки допускаются). |
||
41 | 5) Используемые файлы не должны быть сжатыми или разреженными файлами |
||
42 | (актуально для NTFS, для FAT выполнено автоматически). |
||
1065 | Lrz | 43 | |
44 | ===================================================================== |
||
45 | |||
3539 | clevermous | 46 | Документация в тему (ссылки проверялись на валидность 08.08.2008): |
47 | официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx |
||
48 | в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf |
||
49 | русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip |
||
50 | спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys |
||
51 | и file://C:/ntldr либо file://C:/bootmgr |
||
52 | неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543 |
||
53 | официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf |
||
54 | то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf |
||
55 | описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html |
||
56 | официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf |
||
57 | официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx |
||
58 | официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx |
||
59 | формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx |
||
1065 | Lrz | 60 | |
61 | ===================================================================== |
||
62 | |||
3539 | clevermous | 63 | Схема используемой памяти: |
64 | 600-2000 код загрузчика (и данные) |
||
65 | 2000-3000 стек |
||
66 | 3000-3200 сектор MBR |
||
67 | 3200-3400 бутсектор логического диска |
||
68 | 3400-3C00 информация о кэше для таблиц FAT16/FAT32: |
||
69 | для FAT16 - массив на 0x100 байт, каждый байт равен |
||
70 | |||
71 | соответствующий сектор таблицы FAT16; |
||
72 | для FAT32 - 100h входов по 8 байт: 4 байта |
||
73 | (две ссылки - вперёд и назад) для организации L2-списка |
||
74 | всех прочитанных секторов в порядке возрастания |
||
75 | последнего времени использования + 4 байта для номера |
||
76 | сектора; при переполнении кэша выкидывается элемент из |
||
77 | головы списка, то есть тот, к которому дольше всех |
||
78 | не было обращений |
||
79 | 3400-3440 информация о кэше для файловых записей NTFS в |
||
80 | таком же формате, как и кэш для FAT32, но на 8 входов |
||
81 | 3480-34C0 заголовки для кэшей записей индекса NTFS |
||
82 | 3500-3D00 информация о кэшах записей индекса NTFS: с каждой |
||
83 | файловой записью связан свой кэш для |
||
84 | соответствующего индекса |
||
85 | 4000-8000 место для информации об атрибутах для NTFS |
||
86 | 60000-80000 таблица FAT12 / место под таблицу FAT16 / |
||
87 | кэш для таблицы FAT32 / кэш для структур NTFS |
||
88 | 80000-90000 текущий рассматриваемый кластер |
||
89 | 90000-92000 FAT: кэш для корневой папки |
||
90 | 92000-... FAT: кэш для некорневых папок (каждой папке отводится |
||
91 | 2000h байт = 100h входов, одновременно в кэше |
||
92 | может находиться не более 7 папок; |
||
93 | точный размер определяется размером доступной |
||
94 | физической памяти - как правило, непосредственно |
||
95 | перед A0000 размещается EBDA, Extended BIOS Data Area) |
||
1065 | Lrz | 96 | |
97 | ===================================================================== |
||
98 | |||
3539 | clevermous | 99 | Основной процесс загрузки. |
100 | 0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется |
||
101 | размещением команды install=c:\kordldr.win в первой строке config.sys; |
||
102 | при этом основной загрузчик системы загружает kordldr.win как обычный |
||
103 | com-файл, в какой-то сегмент по смещению 100h и передаёт управление |
||
104 | в начало кода (xxxx:0100). |
||
105 | 0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется |
||
106 | добавлением строки наподобие c:\kordldr.win="KordOS" в секцию |
||
107 | [operating systems] файла boot.ini; если загружаемый файл имеет размер |
||
108 | не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS' |
||
109 | (в случае kordldr.win так и есть), то основной загрузчик каждой из |
||
110 | этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт |
||
111 | управление на адрес 0D00:0256. |
||
112 | 0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями |
||
113 | с базой данных основного загрузчика через bcdedit и подробно описана в |
||
114 | инструкции к kordldr.win; основной загрузчик загружает целиком |
||
115 | kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода. |
||
116 | 1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная |
||
117 | им программа окажется в свою очередь загрузчиком, и в этом случае |
||
118 | kordldr.win оказывается в условиях, когда основной загрузчик уже |
||
119 | установил какое-то окружение, в частности, перехватил некоторые |
||
120 | прерывания. Поэтому перед остальными действиями загрузчик должен |
||
121 | восстановить систему в начальное состояние. (При загрузке под |
||
122 | NT-линейкой такой проблемы не возникает, поскольку там основной |
||
123 | загрузчик ничего в системе не трогает.) Поэтому перед собственно |
||
124 | инициализацией KordOS при работе из-под DOS/9x производятся |
||
125 | дополнительные действия. Первым делом kordldr проверяет, какой из |
||
126 | случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт |
||
127 | управление не на начало кода): определяет значение ip (команда call |
||
128 | помещает в стек адрес следующей после call инструкции, команда pop si |
||
129 | выталкивает его в регистр si), и если оно равно 100h, то kordldr |
||
130 | загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения |
||
131 | у пользователя (поскольку в этой схеме kordldr загружается всегда, |
||
132 | он должен оставить возможность продолжить загрузку DOS/9x). Если |
||
133 | пользователь хочет продолжить обычную загрузку, kordldr завершается. |
||
134 | Иначе используется тот факт, что при выдаче прерывания перезагрузки |
||
135 | int 19h система предварительно снимает все свои перехваты BIOSовских |
||
136 | прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что |
||
137 | kordldr устанавливает свой обработчик трассировочного прерывания, |
||
138 | устанавливает флаг трассировки и передаёт управление DOSовскому |
||
139 | обработчику. Обработчик трассировочного прерывания ничего не делает |
||
140 | до тех пор, пока следующей инструкцией не оказывается int 19h, а |
||
141 | в этот момент отбирает управление и продолжает загрузку KordOS. |
||
142 | При этом BIOSовские обработчики восстановлены за исключением, |
||
143 | быть может, прерывания таймера int 8, которое, возможно, восстановлено |
||
144 | до команды jmp far на оригинальный обработчик. В последнем случае его |
||
145 | нужно восстановить явно. |
||
146 | 2. Загрузчик перемещает свой код на адрес 0000:0600. |
||
147 | 3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0, |
||
148 | настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы |
||
149 | все данные можно было адресовать через [bp+N] с однобайтовым N |
||
150 | (в дальнейшем они так и будут адресоваться для освобождения ds и |
||
151 | экономии на размере кода). Разрешает прерывания на случай, если |
||
152 | они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся |
||
153 | с весёлой рожицы (символ с ASCII-кодом 2). |
||
154 | 4. Определяет характеристики жёсткого диска, указанного в качестве |
||
155 | загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h), |
||
156 | если LBA не поддерживается, то определяет геометрию - число дорожек |
||
157 | и число секторов на дорожке (функция 8 прерывания 13h), эти параметры |
||
158 | нужны функции чтения с диска. |
||
159 | 5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска. |
||
160 | Цель цикла - для каждого логического диска попытаться загрузиться с |
||
161 | него (действия по загрузке с конкретного логического диска начинаются |
||
162 | с метки not_extended), при ошибке загрузки управление передаётся |
||
163 | назад этому циклу (метка next_partition), и поиск подходящего раздела |
||
164 | продолжается. На выходе заполняется одна переменная partition_start, |
||
165 | имеющая смысл начала текущего рассматриваемого логического диска, |
||
166 | но по ходу дела из-за приколов таблиц разделов используются ещё четыре |
||
167 | переменных. cur_partition_ofs - фактически счётчик цикла, формально |
||
168 | указатель на текущий вход в текущей загрузочной записи. Сама |
||
169 | загрузочная запись считывается в память начиная с адреса 3000h. |
||
170 | Три оставшихся нужны для правильной работы с расширенными разделами. |
||
171 | В каждой загрузочной записи помещается не более 4 записей о разделах. |
||
172 | Поэтому главной загрузочной записи, размещающейся в первом физическом |
||
173 | секторе диска, может не хватить, и обычно создаётся так называемый |
||
174 | расширенный раздел с расширенными загрузочными записями, формат |
||
175 | которых почти идентичен главной. Расширенный раздел может быть только |
||
176 | один, но в нём может быть много логических дисков и расширенных |
||
177 | загрузочных записей. Расширенные загрузочные записи организованы |
||
178 | в односвязный список, в каждой такой записи первый вход указывает |
||
179 | на соответствующий логический диск, а второй - на следующую расширенную |
||
180 | загрузочную запись. |
||
181 | При этом в главной загрузочной записи все адреса разделов являются |
||
182 | абсолютными номерами секторов. В расширенных же записях адреса разделов |
||
183 | относительны, причём с разными базами: адрес логического диска |
||
184 | указывается относительно расширенной записи, а адрес следующей |
||
185 | расширенной записи указывается относительно начала расширенного |
||
186 | раздела. Такой разнобой выглядит несколько странно, но имеет место |
||
187 | быть. Три оставшихся переменных содержат: extended_part_start - |
||
188 | начало расширенного раздела; extended_parent - текущая рассматриваемая |
||
189 | расширенная загрузочная запись; extended_part_cur - следующая |
||
190 | загрузочная запись для рассмотрения. |
||
191 | Цикл выглядит так: просматриваются все разделы, указанные в текущей |
||
192 | (главной или расширенной) загрузочной записи; для нормальных разделов |
||
193 | (они же логические диски) происходит переход на not_extended, где |
||
194 | устанавливается partition_start и начинается собственно загрузка |
||
195 | (последующие шаги); при встрече с разделом, тип которого указывает |
||
196 | на расширенность (5 или 0xF), код запоминает начало этого раздела |
||
197 | (в главной загрузочной записи такой тип означает расширенный раздел, |
||
198 | в расширенной - только указатель на следующую расширенную запись, |
||
199 | в обоих случаях он может встретиться только один раз в данной записи); |
||
200 | когда код доходит до конца списка, все нормальные разделы, описываемые |
||
201 | в этой записи, уже просмотрены, так что код с чистой совестью переходит |
||
202 | к следующей расширенной записи. Если он её не встретил, значит, уже |
||
203 | все логические разделы были подвергнуты попыткам загрузиться, и все |
||
204 | безрезультатно, так что выводится ругательство и работа останавливается |
||
1065 | Lrz | 205 | (jmp $). |
3539 | clevermous | 206 | Может возникнуть вопрос, зачем нужна такая сложная схема и почему |
207 | нельзя узнать нужный логический диск заранее или хотя бы ограничиться |
||
208 | первым попавшимся логическим диском, не крутя цикл. Так вот, вариант |
||
209 | с предварительным определением нужного раздела в данном случае не |
||
210 | используется, поскольку повлёк бы за собой нетривиальные лишние |
||
211 | действия по установке (в текущем виде установку можно провести вручную, |
||
212 | и она сводится к указанию системному загрузчику на существование |
||
213 | kordldr); кстати, в альтернативной версии загрузки после |
||
214 | Windows-загрузчика, когда установка осуществляется не вручную, а |
||
215 | специальной программой под Windows, используется модифицированная |
||
216 | версия, в которой как раз начальный физический сектор нужного раздела |
||
217 | прописывается установщиком. Сам kordldr не может установить, с какого |
||
218 | раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан |
||
219 | быть файлом на диске C:\). Вариант с первым попавшимся логическим |
||
220 | диском был реализован в первой версии загрузчика, но по ходу дела |
||
221 | обнаружилось, что таки нужно крутить цикл: во-вторых, может быть |
||
222 | приятным, что сама система может стоять вовсе не на системном C:\, а и |
||
223 | на других дисках; во-первых, диск C: может и не быть первым логическим |
||
224 | разделом - Vista любит создавать скрытый первичный раздел перед |
||
225 | системным, и тогда диск C: становится вторым логическим. |
||
226 | 6. Извещает пользователя о том, что происходит попытка загрузки с очередного |
||
227 | логического диска. |
||
228 | 7. Читает первый сектор логического диска и определяет файловую систему. |
||
229 | И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе |
||
230 | и должно совпадать с характеристикой физического носителя, то есть |
||
231 | 200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число |
||
232 | секторов в кластере и должно быть степенью двойки. |
||
233 | Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со |
||
234 | смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано |
||
235 | быть ненулевым). |
||
236 | Критерий FAT: загрузчик вычисляет число кластеров, определяет |
||
237 | предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению |
||
238 | +38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29). |
||
239 | После определения типа файловой системы извещает пользователя об |
||
240 | определённом типе. Если файловая система не распознана, выдаёт |
||
241 | соответствующее сообщение и переходит к следующему логическому диску. |
||
242 | 8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы - |
||
243 | константу '12'; устанавливает указатель на функцию получения следующего |
||
244 | в цепочке FAT кластера на FAT12-обработчик; считывает в память всю |
||
245 | таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке |
||
246 | чтения пытается использовать другие копии FAT. |
||
247 | 8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы - |
||
248 | константу '16'; устанавливает указатель на функцию получения следующего |
||
249 | в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию |
||
250 | о кэше секторов FAT (массив байт с возможными значениями 0 и 1, |
||
251 | означающими, был ли уже загружен соответствующий сектор - всего в |
||
252 | таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не |
||
253 | загружен, все байты нулевые. |
||
254 | 8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы - |
||
255 | константу '32'; устанавливает указатель на функцию получения следующего |
||
256 | в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию |
||
257 | о кэше секторов FAT (формат информации описан выше, в распределении |
||
258 | используемой загрузчиком памяти) - ни один сектор ещё не загружен. |
||
259 | 8г. Общее для FAT-томов: определяет значения служебных переменных |
||
260 | root_start (первый сектор корневого каталога в FAT12/16, игнорируется |
||
261 | при обработке FAT32-томов), data_start (начало данных с поправкой, |
||
262 | вводимой для того, чтобы кластер N начинался с сектора |
||
263 | N*sectors_per_cluster+data_start), root_clus (первый кластер корневого |
||
264 | каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию |
||
265 | загрузки файла на FAT-обработчик. |
||
266 | 8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы - |
||
267 | константу 'nt'; определяет значение служебной переменной frs_size |
||
268 | (размер в байтах файловой записи, File Record Segment), для полной |
||
269 | корректности проверяет, что это значение (равное 0x400 байт на всех |
||
270 | реальных NTFS-томах - единственный способ изменить его заключается |
||
271 | в пересоздании всех системных структур вручную) не превосходит 0x1000 |
||
272 | и кратно размеру сектора 0x200 байт; инициализирует кэш файловых |
||
273 | записей - ничего ещё не загружено; считывает первый кластер $MFT |
||
274 | и загружает информацию о расположении на диске всей таблицы $MFT |
||
275 | (атрибут 0x80, $Data); устанавливает указатель на функцию загрузки |
||
276 | файла на NTFS-обработчик. |
||
277 | 9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного |
||
278 | загрузчика. При обнаружении ошибки переходит на обработчик ошибок с |
||
279 | соответствующим сообщением. |
||
280 | 10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск), |
||
281 | ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h), |
||
282 | может быть изменён путём модификации константы в исходнике или |
||
283 | специальным установщиком), bx=идентификатор файловой системы (берётся |
||
284 | из стека, куда ранее был засунут на шаге 8), ds:si=указатель на |
||
285 | callback-функцию. |
||
286 | 11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000. |
||
1065 | Lrz | 287 | |
3539 | clevermous | 288 | Функция обратного вызова для вторичного загрузчика: |
289 | предоставляет возможность чтения файла. |
||
290 | Вход и выход описаны в спецификации на загрузчик. |
||
291 | Чтение файла: |
||
292 | 1. Сохраняет стек вызывающего кода и устанавливает свой стек: |
||
293 | ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным |
||
294 | кодом должна указывать на 0:dat. |
||
295 | 2. Разбирает переданные параметры и вызывает процедуру загрузки файла. |
||
296 | 3. Восстанавливает стек вызывающего кода и возвращает управление. |
||
1065 | Lrz | 297 | |
3539 | clevermous | 298 | Вспомогательные процедуры. |
299 | Процедура чтения секторов (read): |
||
300 | на входе должно быть установлено: |
||
1065 | Lrz | 301 | ss:bp = 0:dat |
3539 | clevermous | 302 | es:bx = указатель на начало буфера, куда будут прочитаны данные |
303 | eax = стартовый сектор (относительно начала логического диска) |
||
304 | cx = число секторов (должно быть больше нуля) |
||
305 | на выходе: es:bx указывает на конец буфера, в который были прочитаны данные, |
||
306 | флаг CF установлен, если возникла ошибка чтения |
||
307 | 1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на |
||
308 | устройстве, прибавляя номер первого сектора логического диска, |
||
309 | найденный при переборе дисков. |
||
310 | 2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации |
||
311 | CHS-версия: все читаемые секторы были на одной дорожке. |
||
312 | LBA-версия: число читаемых секторов не превосходило 7Fh (требование |
||
313 | спецификации EDD BIOS). |
||
314 | CHS-версия: |
||
315 | 3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как |
||
316 | единица плюс остаток от деления абсолютного номера на число секторов |
||
317 | на дорожке; дорожка рассчитывается как остаток от деления частного, |
||
318 | полученного на предыдущем шаге, на число дорожек, а цилиндр - как |
||
319 | частное от этого же деления. Если число секторов для чтения больше, |
||
320 | чем число секторов до конца дорожки, уменьшает число секторов для |
||
321 | чтения. |
||
322 | 4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов, |
||
323 | dh=головка, (младшие 6 бит cl)=сектор, |
||
324 | (старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер). |
||
325 | 5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска |
||
326 | и повторяет попытку чтения, всего делается не более трёх попыток |
||
327 | (несколько попыток нужно в случае дискеты для гарантии того, что |
||
328 | мотор раскрутился). Если все три раза происходит ошибка чтения, |
||
329 | переходит на код обработки ошибок с сообщением "Read error". |
||
330 | 6. В соответствии с числом прочитанных на текущей итерации секторов |
||
331 | корректирует текущий сектор, число оставшихся секторов и указатель на |
||
332 | буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает |
||
333 | работу, иначе возвращается на шаг 3. |
||
334 | LBA-версия: |
||
335 | 3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей |
||
336 | итерации) до 7Fh. |
||
337 | 4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами |
||
338 | push, причём в обратном порядке: стек - структура LIFO, и данные в |
||
339 | стеке хранятся в обратном порядке по отношению к тому, как их туда |
||
340 | клали). |
||
341 | 5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки |
||
342 | ошибок с сообщением "Read error". Очищает стек от пакета, |
||
343 | сформированного на предыдущем шаге. |
||
344 | 6. В соответствии с числом прочитанных на текущей итерации секторов |
||
345 | корректирует текущий сектор, число оставшихся секторов и указатель на |
||
346 | буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает |
||
347 | работу, иначе возвращается на шаг 3. |
||
1065 | Lrz | 348 | |
3539 | clevermous | 349 | Процедура обработки ошибок (find_error_si и find_error_sp): |
350 | на входе: указатель на сообщение об ошибке в si либо на верхушке стека |
||
351 | 0. Если вызывается find_error_si, она помещает переданный указатель в стек. |
||
352 | 1. Если ошибка произошла в процессе работы callback-функции, то |
||
353 | (метка error_in_callback) обработчик просто возвращает управление |
||
354 | вызвавшему коду, рапортуя о ненайденном файле. |
||
355 | 2. Если же ошибка произошла до передачи управления вторичному загрузчику, |
||
356 | обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>" |
||
357 | и (восстановив стек) переходит к следующему логическому диску. |
||
1065 | Lrz | 358 | |
3539 | clevermous | 359 | Процедура чтения файла/атрибута по известному размещению на диске |
1065 | Lrz | 360 | (read_file_chunk): |
3539 | clevermous | 361 | на входе должно быть установлено: |
362 | ds:si = указатель на информацию о размещении |
||
363 | es:bx = указатель на начало буфера, куда будут прочитаны данные |
||
364 | ecx = лимит числа секторов для чтения, старшее слово должно быть 0 |
||
365 | на выходе: es:bx указывает на конец буфера, в который были прочитаны данные, |
||
366 | флаг CF установлен, если возникла ошибка чтения |
||
367 | 1. Определяет, является ли атрибут резидентным (возможно только в NTFS |
||
368 | и означает, что данные файла/атрибута уже были целиком прочитаны при |
||
369 | обработке информации о файле) или нерезидентным (означает, что данные |
||
370 | хранятся где-то на диске, и имеется информация о том, где именно). |
||
371 | 2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует |
||
372 | данные по месту назначения (с учётом указанного лимита). |
||
373 | 3. Для нерезидентных атрибутов информация состоит из пар <размер очередного |
||
374 | фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура |
||
375 | читает фрагменты, пока файл не закончится или пока не будет достигнут |
||
376 | указанный лимит. |
||
1065 | Lrz | 377 | |
3539 | clevermous | 378 | Процедура просмотра кэша (cache_lookup): |
379 | на входе должно быть установлено: |
||
380 | eax = искомое значение |
||
381 | ss:si = указатель на структуру-заголовок кэша |
||
382 | на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение |
||
383 | было только что добавлено, и сброшен, если оно уже было в кэше. |
||
384 | 1. Просматривает кэш в поисках указанного значения. Если значение найдено |
||
385 | (при этом флаг CF оказывается сброшенным), переходит к шагу 4. |
||
386 | 2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в |
||
387 | голове двусвязного списка), иначе добавляет к кэшу ещё один вход. |
||
388 | 3. Устанавливает в полученном входе указанное значение. Устанавливает флаг |
||
389 | CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5. |
||
390 | 4. Удаляет вход из списка. |
||
391 | 5. Добавляет сектор в конец списка (самый новый вход).размер>ошибка>текущий> |