]> git.proxmox.com Git - grub2.git/blame - kern/disk.c
2003-01-03 Yoshinori K. Okuji <okuji@enbug.org>
[grub2.git] / kern / disk.c
CommitLineData
6a161fa9 1/*
2 * PUPA -- Preliminary Universal Programming Architecture for GRUB
3 * Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
4 *
5 * PUPA is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with PUPA; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include <pupa/disk.h>
21#include <pupa/err.h>
22#include <pupa/mm.h>
23#include <pupa/types.h>
24#include <pupa/machine/partition.h>
25#include <pupa/misc.h>
26
27/* Disk cache. */
28struct pupa_disk_cache
29{
30 unsigned long id;
31 unsigned long sector;
32 char *data;
33 int lock;
34};
35
36static struct pupa_disk_cache pupa_disk_cache_table[PUPA_DISK_CACHE_NUM];
37
38#if 0
39static unsigned long pupa_disk_cache_hits;
40static unsigned long pupa_disk_cache_misses;
41
42void
43pupa_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
44{
45 *hits = pupa_disk_cache_hits;
46 *misses = pupa_disk_cache_misses;
47}
48#endif
49
50static unsigned
51pupa_disk_cache_get_index (unsigned long id, unsigned long sector)
52{
53 return ((id * 2606459 + (sector >> PUPA_DISK_CACHE_BITS))
54 % PUPA_DISK_CACHE_NUM);
55}
56
57static void
58pupa_disk_cache_invalidate (unsigned long id, unsigned long sector)
59{
60 unsigned index;
61 struct pupa_disk_cache *cache;
62
63 sector &= ~(PUPA_DISK_CACHE_SIZE - 1);
64 index = pupa_disk_cache_get_index (id, sector);
65 cache = pupa_disk_cache_table + index;
66
67 if (cache->id == id && cache->sector == sector && cache->data)
68 {
69 cache->lock = 1;
70 pupa_free (cache->data);
71 cache->data = 0;
72 cache->lock = 0;
73 }
74}
75
76void
77pupa_disk_cache_invalidate_all (void)
78{
79 unsigned i;
80
81 for (i = 0; i < PUPA_DISK_CACHE_NUM; i++)
82 {
83 struct pupa_disk_cache *cache = pupa_disk_cache_table + i;
84
85 if (cache->data && ! cache->lock)
86 {
87 pupa_free (cache->data);
88 cache->data = 0;
89 }
90 }
91}
92
93static char *
94pupa_disk_cache_fetch (unsigned long id, unsigned long sector)
95{
96 struct pupa_disk_cache *cache;
97 unsigned index;
98
99 index = pupa_disk_cache_get_index (id, sector);
100 cache = pupa_disk_cache_table + index;
101
102 if (cache->id == id && cache->sector == sector)
103 {
104 cache->lock = 1;
105#if 0
106 pupa_disk_cache_hits++;
107#endif
108 return cache->data;
109 }
110
111#if 0
112 pupa_disk_cache_misses++;
113#endif
114
115 return 0;
116}
117
118static void
119pupa_disk_cache_unlock (unsigned long id, unsigned long sector)
120{
121 struct pupa_disk_cache *cache;
122 unsigned index;
123
124 index = pupa_disk_cache_get_index (id, sector);
125 cache = pupa_disk_cache_table + index;
126
127 if (cache->id == id && cache->sector == sector)
128 cache->lock = 0;
129}
130
131static pupa_err_t
132pupa_disk_cache_store (unsigned long id, unsigned long sector,
133 const char *data)
134{
135 unsigned index;
136 struct pupa_disk_cache *cache;
137
138 pupa_disk_cache_invalidate (id, sector);
139
140 index = pupa_disk_cache_get_index (id, sector);
141 cache = pupa_disk_cache_table + index;
142
143 cache->data = pupa_malloc (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
144 if (! cache->data)
145 return pupa_errno;
146
147 pupa_memcpy (cache->data, data,
148 PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
149 cache->id = id;
150 cache->sector = sector;
151
152 return PUPA_ERR_NONE;
153}
154
155\f
156
157static pupa_disk_dev_t pupa_disk_dev_list;
158
159void
160pupa_disk_dev_register (pupa_disk_dev_t dev)
161{
162 dev->next = pupa_disk_dev_list;
163 pupa_disk_dev_list = dev;
164}
165
166void
167pupa_disk_dev_unregister (pupa_disk_dev_t dev)
168{
169 pupa_disk_dev_t *p, q;
170
171 for (p = &pupa_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
172 if (q == dev)
173 {
174 *p = q->next;
175 break;
176 }
177}
178
179void
180pupa_disk_dev_iterate (int (*hook) (const char *name))
181{
182 pupa_disk_dev_t p;
183
184 for (p = pupa_disk_dev_list; p; p = p->next)
185 if ((p->iterate) (hook))
186 break;
187}
188
189pupa_disk_t
190pupa_disk_open (const char *name)
191{
192 char *p;
193 pupa_disk_t disk;
194 pupa_disk_dev_t dev;
195 char *raw = (char *) name;
196
197 disk = (pupa_disk_t) pupa_malloc (sizeof (*disk));
198 if (! disk)
199 return 0;
200
201 disk->dev = 0;
202 disk->read_hook = 0;
203 disk->partition = 0;
204 disk->data = 0;
205 disk->name = pupa_strdup (name);
206 if (! disk->name)
207 goto fail;
208
209 p = pupa_strchr (name, ',');
210 if (p)
211 {
212 pupa_size_t len = p - name;
213
214 raw = pupa_malloc (len + 1);
215 if (! raw)
216 goto fail;
217
218 pupa_memcpy (raw, name, len);
219 raw[len] = '\0';
220 }
221
222 for (dev = pupa_disk_dev_list; dev; dev = dev->next)
223 {
224 if ((dev->open) (raw, disk) == PUPA_ERR_NONE)
225 break;
226 else if (pupa_errno == PUPA_ERR_UNKNOWN_DEVICE)
227 pupa_errno = PUPA_ERR_NONE;
228 else
229 goto fail;
230 }
231
232 if (! dev)
233 {
234 pupa_error (PUPA_ERR_UNKNOWN_DEVICE, "no such disk");
235 goto fail;
236 }
237
238 if (p && ! disk->has_partitions)
239 {
240 pupa_error (PUPA_ERR_BAD_DEVICE, "no partition on this disk");
241 goto fail;
242 }
243
244 disk->dev = dev;
245
246 if (p)
247 disk->partition = pupa_partition_probe (disk, p + 1);
248
249 fail:
250
251 if (raw && raw != name)
252 pupa_free (raw);
253
254 if (pupa_errno != PUPA_ERR_NONE)
255 {
256 pupa_disk_close (disk);
257 return 0;
258 }
259
260 return disk;
261}
262
263void
264pupa_disk_close (pupa_disk_t disk)
265{
266 if (disk->dev && disk->dev->close)
267 (disk->dev->close) (disk);
268
269 pupa_free (disk->partition);
270 pupa_free ((void *) disk->name);
271 pupa_free (disk);
272}
273
274static pupa_err_t
275pupa_disk_check_range (pupa_disk_t disk, unsigned long *sector,
276 unsigned long *offset, pupa_ssize_t size)
277{
278 *sector += *offset >> PUPA_DISK_SECTOR_BITS;
279 *offset &= PUPA_DISK_SECTOR_SIZE - 1;
280
281 if (disk->partition)
282 {
283 unsigned long start, len;
284
285 start = pupa_partition_get_start (disk->partition);
286 len = pupa_partition_get_len (disk->partition);
287
288 if (*sector >= len
289 || len - *sector < ((*offset + size + PUPA_DISK_SECTOR_SIZE - 1)
290 >> PUPA_DISK_SECTOR_BITS))
291 return pupa_error (PUPA_ERR_OUT_OF_RANGE, "out of partition");
292
293 *sector += start;
294 }
295
296 if (disk->total_sectors <= *sector
297 || ((*offset + size + PUPA_DISK_SECTOR_SIZE - 1)
298 >> PUPA_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
299 return pupa_error (PUPA_ERR_OUT_OF_RANGE, "out of disk");
300
301 return PUPA_ERR_NONE;
302}
303
304/* Read data from the disk. */
305pupa_err_t
306pupa_disk_read (pupa_disk_t disk, unsigned long sector,
307 unsigned long offset, unsigned long size, char *buf)
308{
309 char *tmp_buf;
310
311 /* First of all, check if the region is within the disk. */
312 if (pupa_disk_check_range (disk, &sector, &offset, size) != PUPA_ERR_NONE)
313 return pupa_errno;
314
315 /* Allocate a temporary buffer. */
316 tmp_buf = pupa_malloc (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
317 if (! tmp_buf)
318 return pupa_errno;
319
320 /* Until SIZE is zero... */
321 while (size)
322 {
323 char *data;
324 unsigned long start_sector;
325 unsigned long len;
326 unsigned long pos;
327
328 /* For reading bulk data. */
329 start_sector = sector & ~(PUPA_DISK_CACHE_SIZE - 1);
330 pos = (sector - start_sector) << PUPA_DISK_SECTOR_BITS;
331 len = (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS) - pos - offset;
332 if (len > size)
333 len = size;
334
335 /* Fetch the cache. */
336 data = pupa_disk_cache_fetch (disk->id, start_sector);
337 if (data)
338 {
339 /* Just copy it! */
340 pupa_memcpy (buf, data + pos + offset, len);
341 pupa_disk_cache_unlock (disk->id, start_sector);
342 }
343 else
344 {
345 /* Otherwise read data from the disk actually. */
346 if ((disk->dev->read) (disk, start_sector,
347 PUPA_DISK_CACHE_SIZE, tmp_buf)
348 != PUPA_ERR_NONE)
349 {
350 /* Uggh... Failed. Instead, just read necessary data. */
351 unsigned num;
352
353 /* If more data is required, no way. */
354 if (pos + size
355 >= (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS))
356 goto finish;
357
358 num = ((size + PUPA_DISK_SECTOR_SIZE - 1)
359 >> PUPA_DISK_SECTOR_BITS);
360 if ((disk->dev->read) (disk, sector, num, tmp_buf))
361 goto finish;
362
363 pupa_memcpy (buf, tmp_buf + offset, size);
364
365 /* Call the read hook, if any. */
366 if (disk->read_hook)
367 while (size)
368 {
369 (disk->read_hook) (sector, offset,
370 ((size > PUPA_DISK_SECTOR_SIZE)
371 ? PUPA_DISK_SECTOR_SIZE
372 : size));
373 sector++;
374 size -= PUPA_DISK_SECTOR_SIZE - offset;
375 offset = 0;
376 }
377
378 /* This must be the end. */
379 goto finish;
380 }
381
382 /* Copy it and store it in the disk cache. */
383 pupa_memcpy (buf, tmp_buf + pos + offset, len);
384 pupa_disk_cache_store (disk->id, start_sector, tmp_buf);
385 }
386
387 /* Call the read hook, if any. */
388 if (disk->read_hook)
389 {
390 unsigned long s = sector;
391 unsigned long l = len;
392
393 while (l)
394 {
395 (disk->read_hook) (s, offset,
396 ((l > PUPA_DISK_SECTOR_SIZE)
397 ? PUPA_DISK_SECTOR_SIZE
398 : l));
1cc73a62 399
400 if (l < PUPA_DISK_SECTOR_SIZE - offset)
401 break;
402
6a161fa9 403 s++;
404 l -= PUPA_DISK_SECTOR_SIZE - offset;
405 offset = 0;
406 }
407 }
408
409 sector = start_sector + PUPA_DISK_CACHE_SIZE;
410 buf += len;
411 size -= len;
412 offset = 0;
413 }
414
415 finish:
416
417 pupa_free (tmp_buf);
418
419 return pupa_errno;
420}
421
422pupa_err_t
423pupa_disk_write (pupa_disk_t disk, unsigned long sector,
424 unsigned long offset, unsigned long size, const char *buf)
425{
426 if (pupa_disk_check_range (disk, &sector, &offset, size) != PUPA_ERR_NONE)
427 return -1;
428
429 while (size)
430 {
431 if (offset != 0 || (size < PUPA_DISK_SECTOR_SIZE && size != 0))
432 {
433 char tmp_buf[PUPA_DISK_SECTOR_SIZE];
434 unsigned long len;
435
436 if (pupa_disk_read (disk, sector, 0, PUPA_DISK_SECTOR_SIZE, tmp_buf)
437 != PUPA_ERR_NONE)
438 goto finish;
439
440 len = PUPA_DISK_SECTOR_SIZE - offset;
441 if (len > size)
442 len = size;
443
444 pupa_memcpy (tmp_buf + offset, buf, len);
445
446 pupa_disk_cache_invalidate (disk->id, sector);
447
448 if ((disk->dev->write) (disk, sector, 1, tmp_buf) != PUPA_ERR_NONE)
449 goto finish;
450
451 sector++;
452 buf += len;
453 size -= len;
454 offset = 0;
455 }
456 else
457 {
458 unsigned long len;
459 unsigned long n;
460
461 len = size & ~(PUPA_DISK_SECTOR_SIZE - 1);
462 n = size >> PUPA_DISK_SECTOR_BITS;
463
464 if ((disk->dev->write) (disk, sector, n, buf) != PUPA_ERR_NONE)
465 goto finish;
466
467 while (n--)
468 pupa_disk_cache_invalidate (disk->id, sector++);
469
470 buf += len;
471 size -= len;
472 }
473 }
474
475 finish:
476
477 return pupa_errno;
478}