]>
Commit | Line | Data |
---|---|---|
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. */ | |
28 | struct pupa_disk_cache | |
29 | { | |
30 | unsigned long id; | |
31 | unsigned long sector; | |
32 | char *data; | |
33 | int lock; | |
34 | }; | |
35 | ||
36 | static struct pupa_disk_cache pupa_disk_cache_table[PUPA_DISK_CACHE_NUM]; | |
37 | ||
38 | #if 0 | |
39 | static unsigned long pupa_disk_cache_hits; | |
40 | static unsigned long pupa_disk_cache_misses; | |
41 | ||
42 | void | |
43 | pupa_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 | ||
50 | static unsigned | |
51 | pupa_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 | ||
57 | static void | |
58 | pupa_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 | ||
76 | void | |
77 | pupa_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 | ||
93 | static char * | |
94 | pupa_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 | ||
118 | static void | |
119 | pupa_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 | ||
131 | static pupa_err_t | |
132 | pupa_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 | ||
157 | static pupa_disk_dev_t pupa_disk_dev_list; | |
158 | ||
159 | void | |
160 | pupa_disk_dev_register (pupa_disk_dev_t dev) | |
161 | { | |
162 | dev->next = pupa_disk_dev_list; | |
163 | pupa_disk_dev_list = dev; | |
164 | } | |
165 | ||
166 | void | |
167 | pupa_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 | ||
179 | void | |
180 | pupa_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 | ||
189 | pupa_disk_t | |
190 | pupa_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 | ||
263 | void | |
264 | pupa_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 | ||
274 | static pupa_err_t | |
275 | pupa_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. */ | |
305 | pupa_err_t | |
306 | pupa_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, §or, &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 | ||
422 | pupa_err_t | |
423 | pupa_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, §or, &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 | } |