Subversion Repositories Kolibri OS

Rev

Rev 1635 | Go to most recent revision | 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  nor 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  ''AS IS'' AND ANY
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  BE LIABLE FOR ANY
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
 
3555 Serge 27
					Встречаются вирус и FAT.
28
					- Привет, ты кто?
29
					- Я? Вирус.
30
					- A я AFT, то есть TAF, то есть FTA, черт, совсем запутался...
1065 Lrz 31
 
3555 Serge 32
Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт.
1065 Lrz 33
 
34
=====================================================================
35
 
3555 Serge 36
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
37
выбор осуществляется установкой константы use_lba в первой строке исходника.
38
Требования для работы:
39
1) Сам бутсектор, первая копия FAT и все используемые файлы
40
должны быть читабельны.
41
2) Минимальный процессор - 80186.
42
3) В системе должно быть как минимум 592K свободной базовой памяти.
1065 Lrz 43
 
44
=====================================================================
45
 
3555 Serge 46
Документация в тему (ссылки валидны на момент написания этого файла, 15.05.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
	официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf
51
		то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf
52
	описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html
53
	официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf
1065 Lrz 54
 
55
=====================================================================
56
 
3555 Serge 57
Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер
58
занимает 12 бит в таблице FAT, так что общий размер не превосходит
59
0x17EE = 6126 байт. Вся таблица помещается в памяти.
60
Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый
61
кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит
62
0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в
63
этом случае несколько нецелесообразно считывать всю таблицу, поскольку
64
на практике нужна только небольшая её часть. Поэтому место в памяти
65
резервируется, но данные считываются только в момент, когда к ним
66
действительно идёт обращение.
1065 Lrz 67
 
3555 Serge 68
Схема используемой памяти:
69
	...-7C00	стек
70
	7C00-7E00	код бутсектора
71
	7E00-8200	вспомогательный файл загрузчика (kordldr.f1x)
72
	8200-8300	список загруженных секторов таблицы FAT16
73
			(1 = соответствующий сектор загружен)
74
	60000-80000	загруженная таблица FAT12 / место для таблицы FAT16
75
	80000-90000	текущий кластер текущей рассматриваемой папки
76
	90000-92000	кэш для корневой папки
77
	92000-...	кэш для некорневых папок (каждой папке отводится
78
			2000h байт = 100h входов, одновременно в кэше
79
			может находиться не более 7 папок;
80
			точный размер определяется размером доступной
81
			физической памяти - как правило, непосредственно
82
			перед A0000 размещается EBDA, Extended BIOS Data Area)
1065 Lrz 83
 
84
=====================================================================
85
 
3555 Serge 86
Основной процесс загрузки.
87
Точка входа (start): получает управление от BIOS при загрузке, при этом
88
	dl содержит идентификатор диска, с которого идёт загрузка
89
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
90
	кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
91
	бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
92
	это освобождает ds и экономит на размере кода).
93
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
94
	прерывания 13h. Если нет, переходит на код обработки ошибок с
95
	сообщением об отсутствии LBA.
96
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
97
	записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
98
	предполагает уже существующие данные корректными.
99
3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки
100
	и начальный сектор данных. Кладёт их в стек; впоследствии они
101
	всегда будут лежать в стеке и адресоваться через bp.
102
4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых
103
	секторов - минимум из размера корневой папки, указанного в BPB, и 16
104
	(размер кэша для корневой папки - 2000h байт = 16 секторов).
105
5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если
106
	он оказывается папкой, или если файл имеет нулевую длину -
107
	переходит на код обработки ошибок с сообщением о
108
	ненайденном загрузчике.
109
	Замечание: на этом этапе загрузки искать можно только в корневой
110
	папке и только имена, заданные в формате файловой системе FAT
111
	(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
112
	быть заглавными, при необходимости имя и расширение дополняются
113
	пробелами, разделяющей точки нет, завершающего нуля нет).
114
6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт
115
	ему управление. При этом в регистрах dx:ax оказывается абсолютный
116
	номер первого сектора kordldr.f1x, а в cx - число считанных секторов
117
	(равное размеру кластера).
1065 Lrz 118
 
3555 Serge 119
Вспомогательные процедуры бутсектора.
120
Код обработки ошибок (err):
121
1. Выводит строку с сообщением об ошибке.
122
2. Выводит строку "Press any key...".
123
3. Ждёт нажатия any key.
124
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
125
5. Для подстраховки зацикливается.
1065 Lrz 126
 
3555 Serge 127
Процедура чтения секторов (read_sectors и read_sectors2):
128
на входе должно быть установлено:
1065 Lrz 129
	ss:bp = 0:7C00
3555 Serge 130
	es:bx = указатель на начало буфера, куда будут прочитаны данные
131
	dx:ax = стартовый сектор (относительно начала логического диска
132
		для read_sectors, относительно начала данных для read_sectors2)
133
	cx = число секторов (должно быть больше нуля)
134
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
135
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
136
	в номер относительно начала логического диска, прибавляя номер сектора
137
	начала данных, хранящийся в стеке как [bp-8].
138
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
139
	устройстве, прибавляя значение соответствующего поля из BPB.
140
2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации
141
	CHS-версия: все читаемые секторы были на одной дорожке.
142
	LBA-версия: число читаемых секторов не превосходило 7Fh (требование
143
	спецификации EDD BIOS).
144
CHS-версия:
145
3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как
146
	единица плюс остаток от деления абсолютного номера на число секторов
147
	на дорожке; дорожка рассчитывается как остаток от деления частного,
148
	полученного на предыдущем шаге, на число дорожек, а цилиндр - как
149
	частное от этого же деления. Если число секторов для чтения больше,
150
	чем число секторов до конца дорожки, уменьшает число секторов для
151
	чтения.
152
4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов,
153
	dh=головка, (младшие 6 бит cl)=сектор,
154
	(старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер).
155
5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска
156
	и повторяет попытку чтения, всего делается не более трёх попыток
157
	(несколько попыток нужно в случае дискеты для гарантии того, что
158
	мотор раскрутился). Если все три раза происходит ошибка чтения,
159
	переходит на код обработки ошибок с сообщением "Read error".
160
6. В соответствии с числом прочитанных на текущей итерации секторов
161
	корректирует текущий сектор, число оставшихся секторов и указатель на
162
	буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
163
	работу, иначе возвращается на шаг 3.
164
LBA-версия:
165
3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
166
	итерации) до 7Fh.
167
4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
168
	push, причём в обратном порядке: стек - структура LIFO, и данные в
169
	стеке хранятся в обратном порядке по отношению к тому, как их туда
170
	клали).
171
5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки
172
	ошибок с сообщением "Read error". Очищает стек от пакета,
173
	сформированного на предыдущем шаге.
174
6. В соответствии с числом прочитанных на текущей итерации секторов
175
	корректирует текущий сектор, число оставшихся секторов и указатель на
176
	буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
177
	работу, иначе возвращается на шаг 3.
1065 Lrz 178
 
3555 Serge 179
Процедура поиска элемента по имени в уже прочитанных данных папки
1065 Lrz 180
	(scan_for_filename):
3555 Serge 181
на входе должно быть установлено:
182
	ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя,
183
		3 на расширение, все буквы заглавные, если имя/расширение
184
		короче, оно дополняется до максимума пробелами)
185
	es = сегмент данных папки
186
	cx = число элементов в прочитанных данных
187
на выходе: ZF определяет, нужно ли продолжать разбор данных папки
188
	(ZF=1, если либо найден запрошенный элемент, либо достигнут
189
	конец папки); CF определяет, удалось ли найти элемент с искомым именем
190
	(CF=1, если не удалось); если удалось, то es:di указывает на него.
191
scan_for_filename считает, что данные папки размещаются начиная с es:0.
192
Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки
193
проверяет имена.
1065 Lrz 194
 
3555 Serge 195
Процедура поиска элемента в корневой папке (lookup_in_root_dir):
196
на входе должно быть установлено:
1065 Lrz 197
	ss:bp = 0:7C00
3555 Serge 198
	ds:si = указатель на имя файла в формате FAT (см. выше)
199
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
200
	CF сброшен и es:di указывает на элемент папки
201
Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле
202
	сканирует элементы; если по результатам сканирования обнаруживает,
203
	что нужно читать папку дальше, то считывает не более 0x10000 = 64K
204
	байт (ограничение введено по двум причинам: во-первых, чтобы заведомо
205
	не вылезти за пределы используемой памяти, во-вторых, сканирование
206
	предполагает, что все обрабатываемые элементы располагаются в одном
207
	сегменте) и продолжает цикл.
208
Сканирование прекращается в трёх случаях: обнаружен искомый элемент;
209
	кончились элементы в папке (судя по числу элементов, указанному в BPB);
210
	очередной элемент папки сигнализирует о конце (первый байт нулевой).
1065 Lrz 211
 
3555 Serge 212
Процедура вывода на экран ASCIIZ-строки (out_string):
213
на входе: ds:si -> строка
214
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
1065 Lrz 215
 
216
=====================================================================
217
 
3555 Serge 218
Работа вспомогательного загрузчика kordldr.f1x:
219
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
220
	В зависимости от этого устанавливает смещения используемых процедур
221
	бутсектора. Критерий проверки: scan_for_filename должна начинаться
222
	с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может
223
	с равным успехом ассемблироваться и как 33 FF, но fasm генерирует
224
	именно такую форму).
225
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
226
	адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
227
	ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
228
	место должно быть, отсюда ограничение в 592 Kb (94000h байт).
229
	Замечание: этот размер не может превосходить 0A0000h байт и
230
	на практике оказывается немного (на 1-2 килобайта) меньшим из-за
231
	наличия	дополнительной области данных BIOS "вверху" базовой памяти.
232
3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной
233
	спецификации от Microsoft (версия 1.03 спецификации датирована,
234
	к слову, 06 декабря 2000 года), разрядность FAT определяется
235
	исключительно числом кластеров: максимальное число кластеров на
236
	FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12
237
	может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2,
238
	а число 0xFF7 не может быть корректным номером кластера.
239
	Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается
240
	по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает
241
	явно неверно, считая, что 0xFF6 (или меньше) кластеров означает
242
	FAT12-том, в результате получается, что последний кластер
243
	(в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe
244
	[встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик
245
	[бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы
246
	лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили
247
	в соответствии со спецификацией. Linux при определении FAT12/FAT16
248
	честно следует спецификации.
249
	Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT
250
	Microsoft если и будет исправлять ошибки, то согласно собственному
251
	описанию.
252
4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000.
253
	Если размер, указанный в BPB, превосходит 12 секторов,
254
	это означает, что заявленный размер слишком большой (это не считается
255
	ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12
256
	заведомо влезает в такой объём данных).
257
Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор
258
	FAT не загружен (они будут подгружаться позднее, когда понадобятся
259
	и только те, которые понадобятся).
260
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
261
	kordldr.f1x, и загрузчик подгружает вторую свою часть, используя
262
	значения регистров на входе в kordldr.f1x.
263
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
264
	найден,	или оказался папкой, или оказался слишком большим, то переходит
265
	на код обработки ошибок из бутсектора с сообщением
1065 Lrz 266
	"Fatal error: cannot load the secondary loader".
3555 Serge 267
	Замечание: на этом этапе имя файла уже можно указывать вместе с путём
268
	и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
269
	по-прежнему нет.
270
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
271
	Это нужно, чтобы последующие обращения к коду бутсектора в случае
272
	ошибок чтения не выводил соответствующее сообщение с последующей
273
	перезагрузкой, а рапортовал об ошибке чтения, которую мог бы
274
	как-нибудь обработать вторичный загрузчик.
275
8. Если загрузочный диск имеет идентификатор меньше 0x80,
276
	то устанавливает al='f' ("floppy"), ah=идентификатор диска,
277
	иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
278
	Устанавливает bx='12', если тип файловой системы - FAT12, и
279
	bx='16' в случае FAT16.	Устанавливает si=смещение функции обратного
280
	вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес.
281
9. Передаёт управление по адресу 1000:0000.
1065 Lrz 282
 
3555 Serge 283
Функция обратного вызова для вторичного загрузчика:
284
	предоставляет возможность чтения файла.
285
Вход и выход описаны в спецификации на загрузчик.
286
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
287
	ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным
288
	кодом должна указывать на 0:7C00, а -8 берётся от того, что
289
	инициализирующий код бутсектора уже поместил в стек 2 двойных слова,
290
	и они должны сохраняться в неизменности.
291
2. Разбирает переданные параметры, выясняет, какое действие запрошено,
292
	и вызывает нужную вспомогательную процедуру.
293
3. Восстанавливает стек вызывающего кода и возвращает управление.
1065 Lrz 294
 
3555 Serge 295
Вспомогательные процедуры kordldr.f1x.
296
Процедура получения следующего кластера в FAT (get_next_cluster):
297
1. Вспоминает разрядность FAT, вычисленную ранее.
298
Для FAT12:
299
2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана
300
	вся таблица FAT.
301
3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте
302
	слова, задающего следующий кластер. Загружает слово по этому адресу.
303
4. Если кластер имеет нечётный номер, то соответствующий ему элемент
304
	располагается в старших 12 битах слова, и слово нужно сдвинуть вправо
305
	на 4 бита; в противном случае - в младших 12 битах, и делать ничего не
306
	надо.
307
5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7:
308
	номера нормальных кластеров меньше, и флаг CF устанавливается;
309
	специальные значения EOF и BadClus сбрасывают флаг CF.
310
Для FAT16:
311
2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных
312
	в таблице FAT.
313
3. Если сектор ещё не загружен, то загружает его.
314
4. Вычисляет смещение данных для конкретного кластера относительно начала
315
	сектора.
316
5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3.
317
6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг
318
	CF устанавливается; специальные значения EOF и BadClus сбрасывают CF.
1065 Lrz 319
 
3555 Serge 320
Процедура загрузки файла (load_file):
321
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
322
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
323
	разделяются символом '/') в FAT-формат 8+3. Если это невозможно
324
	(больше 8 символов в имени, больше 3 символов в расширении или
325
	больше одной точки), возвращается с ошибкой.
326
3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой
327
	папки используется процедура из бутсектора. Для остальных папок:
328
	a) Проверяет, есть ли такая папка в кэше некорневых папок.
329
	(Идентификация папок осуществляется по номеру начального кластера.)
330
	Если такой папки ещё нет, добавляет её в кэш; если тот переполняется,
331
	выкидывает папку, к которой дольше всего не было обращений. (Для
332
	каждого элемента кэша хранится метка от 0 до (размер кэша)-1,
333
	определяющая его номер при сортировке по давности последнего обращения.
334
	При обращении к какому-то элементу его метка становится нулевой,
335
	а те метки, которые меньше старого значения, увеличиваются на единицу.)
336
	б) Просматривает в поисках запрошенного имени все элементы из кэша,
337
	используя процедуру из бутсектора. Если обнаруживает искомый элемент,
338
	переходит к шагу 4. Если обнаруживает конец папки, возвращается из
339
	процедуры с ошибкой.
340
	в) В цикле считывает папку посекторно. При этом пропускает начальные
341
	секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
342
	прочитанный сектор копирует в кэш, если там ещё остаётся место,
343
	и просматривает в нём все элементы. Работает, пока не случится одно из
344
	трёх событий: найден искомый элемент; кончились кластеры (судя по
345
	цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
346
	(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
347
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
348
	запрошенном имени должен быть файлом, все промежуточные - папками.
349
	Если текущий компонент имени - промежуточный, продвигает текущую
350
	рассматриваемую папку и возвращается к пункту 2.
351
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
352
	при вызове буфер последовательными вызовами функции бутсектора;
353
	при этом если несколько кластеров файла расположены на диске
354
	последовательно, то их чтение объединяется в одну операцию.
355
	Следит за тем, чтобы не превысить указанный при вызове процедуры
356
	лимит числа секторов для чтения.
1065 Lrz 357
 
3555 Serge 358
Процедура продолжения загрузки файла (continue_load_file): встроена
359
	внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
360
	сохранённые из load_file) и продолжает шаг 5.