Subversion Repositories Kolibri OS

Rev

Rev 1065 | 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
 
3539 clevermous 27
					Читай между строк - там никогда не бывает опечаток.
1065 Lrz 28
 
3539 clevermous 29
Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт.
1065 Lrz 30
 
31
=====================================================================
32
 
3539 clevermous 33
Есть две версии в зависимости от того, поддерживает ли носитель LBA,
34
выбор осуществляется установкой константы use_lba в первой строке исходника.
35
Требования для работы:
36
1) Сам бутсектор, первая копия FAT и все используемые файлы
37
должны быть читабельны. (Если дело происходит на носителе с разбиением на
38
разделы и загрузочный код в MBR достаточно умный, то читабельности резервной
39
копии бутсектора (сектор номер 6 на томе) достаточно вместо читабельности
40
самого бутсектора).
41
2) Минимальный процессор - 80386.
42
3) В системе должно быть как минимум 584K свободной базовой памяти.
1065 Lrz 43
 
44
=====================================================================
45
 
3539 clevermous 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
 
3539 clevermous 57
Схема используемой памяти:
58
	...-7C00	стек
59
	7C00-7E00	код бутсектора
60
	7E00-8200	вспомогательный файл загрузчика (kordldr.f32)
61
	8400-8C00	информация о кэше для таблицы FAT: 100h входов по 8
62
			байт: 4 байта (две ссылки - вперёд и назад) для
63
			организации L2-списка всех прочитанных секторов в
64
			порядке возрастания последнего времени использования
65
			+ 4 байта для номера сектора; при переполнении кэша
66
			выкидывается элемент из головы списка, то есть тот,
67
			к которому дольше всех не было обращений
68
	60000-80000	кэш для таблицы FAT (100h секторов)
69
	80000-90000	текущий кластер текущей рассматриваемой папки
70
	90000-...	кэш для содержимого папок (каждой папке отводится
71
			2000h байт = 100h входов, одновременно в кэше
72
			может находиться не более 8 папок;
73
			точный размер определяется размером доступной
74
			физической памяти - как правило, непосредственно
75
			перед A0000 размещается EBDA, Extended BIOS Data Area)
1065 Lrz 76
 
77
=====================================================================
78
 
3539 clevermous 79
Основной процесс загрузки.
80
Точка входа (start): получает управление от BIOS при загрузке, при этом
81
	dl содержит идентификатор диска, с которого идёт загрузка
82
1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед
83
	кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало
84
	бутсектора (в дальнейшем данные будут адресоваться через [bp+N] -
85
	это освобождает ds и экономит на размере кода). Сохраняет в стеке
86
	идентификатор загрузочного диска для последующего обращения
87
	через byte [bp-2].
88
2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h
89
	прерывания 13h. Если нет, переходит на код обработки ошибок с
90
	сообщением об отсутствии LBA.
91
CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и
92
	записывает полученные данные поверх BPB. Если вызов завершился ошибкой,
93
	предполагает уже существующие данные корректными.
94
3. Вычисляет начало данных FAT-тома, сохраняет его в стек для последующего
95
	обращения через dword [bp-10]. В процессе вычисления узнаёт начало
96
	первой FAT, сохраняет и его в стек для последующего обращения через
1065 Lrz 97
	dword [bp-6].
3539 clevermous 98
4. (Заканчивая тему параметров в стеке) Помещает в стек dword-значение -1
99
	для последующего обращения через dword [bp-14] - инициализация
100
	переменной, содержащей текущий сектор, находящийся в кэше FAT
101
	(-1 не является валидным значением для номера сектора FAT).
102
5. Ищет в корневой папке элемент kordldr.f32. Если не находит - переходит на
103
	код обработки ошибок с сообщением о ненайденном загрузчике.
104
	Замечание: на этом этапе загрузки искать можно только в корневой
105
	папке и только имена, заданные в формате файловой системе FAT
106
	(8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны
107
	быть заглавными, при необходимости имя и расширение дополняются
108
	пробелами, разделяющей точки нет, завершающего нуля нет).
109
6. Загружает первый кластер файла kordldr.f32 по адресу 0:7E00 и передаёт
110
	ему управление. При этом в регистре eax оказывается абсолютный
111
	номер первого сектора kordldr.f32, а в cx - число считанных секторов
112
	(равное размеру кластера).
1065 Lrz 113
 
3539 clevermous 114
Вспомогательные процедуры бутсектора.
115
Код обработки ошибок (err):
116
1. Выводит строку с сообщением об ошибке.
117
2. Выводит строку "Press any key...".
118
3. Ждёт нажатия any key.
119
4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё.
120
5. Для подстраховки зацикливается.
1065 Lrz 121
 
3539 clevermous 122
Процедура чтения кластера (read_cluster):
123
на входе должно быть установлено:
1065 Lrz 124
	ss:bp = 0:7C00
3539 clevermous 125
	es:bx = указатель на начало буфера, куда будут прочитаны данные
126
	eax = номер кластера
127
на выходе: ecx = число прочитанных секторов (размер кластера),
128
	es:bx указывает на конец буфера, в который были прочитаны данные,
129
	eax и старшие слова других 32-битных регистров разрушаются
130
Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора
131
и переходит к следующей процедуре.
1065 Lrz 132
 
3539 clevermous 133
Процедура чтения секторов (read_sectors32 и read_sectors2):
134
на входе должно быть установлено:
1065 Lrz 135
	ss:bp = 0:7C00
3539 clevermous 136
	es:bx = указатель на начало буфера, куда будут прочитаны данные
137
	eax = стартовый сектор (относительно начала логического диска
138
		для read_sectors32, относительно начала данных
139
		для read_sectors2)
140
	cx = число секторов (должно быть больше нуля)
141
на выходе: es:bx указывает на конец буфера, в который были прочитаны данные
142
	старшие слова 32-битных регистров могут разрушиться
143
0. Если вызывается read_sectors2, она переводит указанный ей номер сектора
144
	в номер относительно начала логического диска, прибавляя номер сектора
145
	начала данных, хранящийся в стеке как [bp-10].
146
1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на
147
	устройстве, прибавляя значение соответствующего поля из BPB.
148
2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации
149
	CHS-версия: все читаемые секторы были на одной дорожке.
150
	LBA-версия: число читаемых секторов не превосходило 7Fh (требование
151
	спецификации EDD BIOS).
152
CHS-версия:
153
3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как
154
	единица плюс остаток от деления абсолютного номера на число секторов
155
	на дорожке; дорожка рассчитывается как остаток от деления частного,
156
	полученного на предыдущем шаге, на число дорожек, а цилиндр - как
157
	частное от этого же деления. Если число секторов для чтения больше,
158
	чем число секторов до конца дорожки, уменьшает число секторов для
159
	чтения.
160
4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов,
161
	dh=головка, (младшие 6 бит cl)=сектор,
162
	(старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер).
163
5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска
164
	и повторяет попытку чтения, всего делается не более трёх попыток
165
	(несколько попыток нужно в случае дискеты для гарантии того, что
166
	мотор раскрутился). Если все три раза происходит ошибка чтения,
167
	переходит на код обработки ошибок с сообщением "Read error".
168
6. В соответствии с числом прочитанных на текущей итерации секторов
169
	корректирует текущий сектор, число оставшихся секторов и указатель на
170
	буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
171
	работу, иначе возвращается на шаг 3.
172
LBA-версия:
173
3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей
174
	итерации) до 7Fh.
175
4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами
176
	push, причём в обратном порядке: стек - структура LIFO, и данные в
177
	стеке хранятся в обратном порядке по отношению к тому, как их туда
178
	клали).
179
5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки
180
	ошибок с сообщением "Read error". Очищает стек от пакета,
181
	сформированного на предыдущем шаге.
182
6. В соответствии с числом прочитанных на текущей итерации секторов
183
	корректирует текущий сектор, число оставшихся секторов и указатель на
184
	буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает
185
	работу, иначе возвращается на шаг 3.
1065 Lrz 186
 
3539 clevermous 187
Процедура поиска элемента в папке (lookup_in_dir):
188
на входе должно быть установлено:
1065 Lrz 189
	ss:bp = 0:7C00
3539 clevermous 190
	ds:si = указатель на имя файла в формате FAT (см. выше)
191
	eax = начальный кластер папки
1065 Lrz 192
	bx = 0
3539 clevermous 193
на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то
194
	CF сброшен и es:di указывает на элемент папки
195
В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных
196
данных. Для чтения кластера использует уже описанную процедуру read_clusters,
197
для продвижения по цепочке кластеров - описанную далее процедуру
198
get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса
199
8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше,
200
если чтение прервётся раньше) не перекрываются последующими чтениями
201
(это будет использовано позднее, в системе кэширования из kordldr.f32).
202
Выход осуществляется в любом из следующих случаев: найден запрошенный элемент;
203
кончились элементы в папке (первый байт очередного элемента нулевой);
204
кончились данные папки в соответствии с цепочкой кластеров из FAT.
1065 Lrz 205
 
3539 clevermous 206
Процедура вывода на экран ASCIIZ-строки (out_string):
207
на входе: ds:si -> строка
208
В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh.
1065 Lrz 209
 
210
=====================================================================
211
 
3539 clevermous 212
Работа вспомогательного загрузчика kordldr.f32:
213
1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора.
214
	В зависимости от этого устанавливает смещения используемых процедур
215
	бутсектора. Критерий проверки: в CHS-версии по адресу err находится
216
	байт 0xE8 (машинная команда call), в LBA-версии по тому же адресу
217
	находится байт 0x14, а адрес процедуры err другой.
218
2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска
219
	адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с
220
	ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента
221
	место должно быть, отсюда ограничение в 592 Kb (94000h байт).
222
	Замечание: этот размер не может превосходить 0A0000h байт и
223
	на практике оказывается немного (на 1-2 килобайта) меньшим из-за
224
	наличия	дополнительной области данных BIOS "вверху" базовой памяти.
225
3. Инициализирует кэширование папок. Бутсектор уже загрузил какую-то часть
226
	данных корневой папки; копирует загруженные данные в кэш и запоминает,
227
	что в кэше есть корневая папка.
228
4. Инициализирует кэширование FAT. Бутсектор имеет дело с FAT в том и только
229
	том случае, когда ему приходится загружать данные корневой папки,
230
	не поместившиеся в один кластер. В этом случае в памяти присутствует
231
	один сектор FAT (если было несколько обращений - последний из
232
	использованных).
233
5. Если кластер равен сектору, то бутсектор загрузил только часть файла
234
	kordldr.f32, и загрузчик подгружает вторую свою часть, используя
235
	значения регистров на входе в kordldr.f32.
236
6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не
237
	найден,	или оказался папкой, или оказался слишком большим, то переходит
238
	на код обработки ошибок из бутсектора с сообщением
1065 Lrz 239
	"Fatal error: cannot load the secondary loader".
3539 clevermous 240
	Замечание: на этом этапе имя файла уже можно указывать вместе с путём
241
	и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов
242
	по-прежнему нет.
243
7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err.
244
	Это нужно, чтобы последующие обращения к коду бутсектора в случае
245
	ошибок чтения не выводил соответствующее сообщение с последующей
246
	перезагрузкой, а рапортовал об ошибке чтения, которую могло бы
247
	как-нибудь обработать ядро.
248
8. Если загрузочный диск имеет идентификатор меньше 0x80,
249
	то устанавливает al='f' ("floppy"), ah=идентификатор диска,
250
	иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска).
251
	(Говорите, дискеток с FAT32 не бывает? В чём-то Вы правы... но
252
	уверены ли Вы, что нет загрузочных устройств, подобных дискетам,
253
	но большего размера, и для которых BIOS-идентификатор меньше 0x80?)
254
	Устанавливает bx='32' (тип файловой системы - FAT32).
255
	Устанавливает si=смещение функции обратного вызова. Поскольку в этот
256
	момент ds=0, то ds:si образуют полный адрес.
257
9. Передаёт управление по адресу 1000:0000.
1065 Lrz 258
 
3539 clevermous 259
Функция обратного вызова для вторичного загрузчика:
260
	предоставляет возможность чтения файла.
261
Вход и выход описаны в спецификации на загрузчик.
262
1. Сохраняет стек вызывающего кода и устанавливает свой стек:
263
	ss:sp = 0:(7C00-10), bp=7C00: пара ss:bp при работе с остальным
264
	кодом должна указывать на 0:7C00, а -10 берётся от того, что
265
	инициализирующий код бутсектора уже поместил в стек 10 байт параметров,
266
	и они должны сохраняться в неизменности. (Значение [ebp-14],
267
	"текущий сектор, находящийся в кэше FAT", не используется после
268
	инициализации кэширования в kordldr.f32.)
269
2. Разбирает переданные параметры и вызывает нужную из вспомогательных
270
	процедур (загрузки файла либо продолжения загрузки файла).
271
3. Восстанавливает стек вызывающего кода и возвращает управление.
1065 Lrz 272
 
3539 clevermous 273
Вспомогательные процедуры kordldr.f32.
274
Процедура получения следующего кластера в FAT (get_next_cluster):
275
1. Вычисляет номер сектора в FAT, в котором находится запрошенный элемент.
276
	(В секторе 0x200 байт, каждый вход занимает 4 байта.)
277
2. Проверяет, есть ли сектор в кэше. Если есть, пропускает шаги 3 и 4.
278
3. Если нет, то в кэш нужно вставить новый элемент. Если кэш ещё не заполнен,
279
	выделяет очередной элемент в конце кэша. Если заполнен, удаляет
280
	самый старый элемент (тот, к которому дольше всего не было обращений);
281
	для того, чтобы отслеживать порядок элементов по времени последнего
282
	обращения, все (выделенные) элементы кэша связаны в двусвязный список,
283
	в котором первым элементом является самый старый, а ссылки вперёд
284
	указывают на следующий по времени последнего обращения.
285
4. Читает соответствующий сектор FAT с диска.
286
5. Корректирует список: текущий обрабатываемый элемент удаляется с той позиции,
287
	где он находится, и добавляется в конец. (В случае со свежедобавленными
288
	в кэш элементами удаления не делается, поскольку их в списке ещё нет.)
289
6. Считывает нужный вход в FAT, сбрасывая старшие 4 бита.
290
7. Сравнивает прочитанное значение с пределом: если оно строго меньше
291
	0x0FFFFFF7, то оно задаёт номер следующего кластера в цепочке;
292
	в противном случае цепочка закончилась.
1065 Lrz 293
 
3539 clevermous 294
Процедура загрузки файла (load_file):
295
1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4.
296
2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты
297
	разделяются символом '/') в FAT-формат 8+3. Если это невозможно
298
	(больше 8 символов в имени, больше 3 символов в расширении или
299
	больше одной точки), возвращается с ошибкой.
300
3. Ищет элемент с таким именем в текущей рассматриваемой папке.
301
	а) Проверяет, есть ли такая папка в кэше папок. (Идентификация папок
302
	осуществляется по номеру начального кластера.) Если такой папки ещё
303
	нет, добавляет её в кэш; если тот переполняется, выкидывает папку,
304
	к которой дольше всего не было обращений. (Для каждого элемента кэша
305
	хранится метка от 0 до (размер кэша)-1, определяющая его номер при
306
	сортировке по давности последнего обращения. При обращении к какому-то
307
	элементу его метка становится нулевой, а те метки, которые меньше
308
	старого значения, увеличиваются на единицу.)
309
	б) Просматривает в поисках запрошенного имени все элементы из кэша,
310
	используя процедуру из бутсектора. Если обнаруживает искомый элемент,
311
	переходит к шагу 4. Если обнаруживает конец папки, возвращается из
312
	процедуры с ошибкой.
313
	в) В цикле считывает папку посекторно. При этом пропускает начальные
314
	секторы, которые уже находятся в кэше и уже были просмотрены. Каждый
315
	прочитанный сектор копирует в кэш, если там ещё остаётся место,
316
	и просматривает в нём все элементы. Работает, пока не случится одно из
317
	трёх событий: найден искомый элемент; кончились кластеры (судя по
318
	цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце
319
	(первый байт нулевой). В двух последних случаях возвращается с ошибкой.
320
4. Проверяет тип найденного элемента (файл/папка): последний элемент в
321
	запрошенном имени должен быть файлом, все промежуточные - папками.
322
	Если текущий компонент имени - промежуточный, продвигает текущую
323
	рассматриваемую папку и возвращается к пункту 2.
324
5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный
325
	при вызове буфер последовательными вызовами функции бутсектора;
326
	при этом если несколько кластеров файла расположены на диске
327
	последовательно, то их чтение объединяется в одну операцию.
328
	Следит за тем, чтобы не превысить указанный при вызове процедуры
329
	лимит числа секторов для чтения.
1065 Lrz 330
 
3539 clevermous 331
Процедура продолжения загрузки файла (continue_load_file): встроена
332
	внутрь шага 5 load_file; загружает в регистры нужные значения (ранее
333
	сохранённые из load_file) и продолжает шаг 5.