Initial code release
[external/syslinux.git] / com32 / gpllib / disk / read.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2009 Pierre-Alexandre Meyer
4  *
5  *   Some parts borrowed from chain.c32:
6  *
7  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
8  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
9  *
10  *   This file is part of Syslinux, and is made available under
11  *   the terms of the GNU General Public License version 2.
12  *
13  * ----------------------------------------------------------------------- */
14
15 #include <com32.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include <disk/errno_disk.h>
20 #include <disk/geom.h>
21 #include <disk/read.h>
22 #include <disk/util.h>
23 #include <disk/common.h>
24
25 /*
26  * TODO: implement file descriptors to cache metadata (geometry, ...)
27  */
28
29 /**
30  * read_mbr - return a pointer to a malloced buffer containing the mbr
31  * @drive:      Drive number
32  * @buf:        Pre-allocated buffer for output
33  *
34  * Return the number of sectors read on success or -1 on failure.
35  * errno_disk contains the error number.
36  **/
37 int read_mbr(int drive, void *buf)
38 {
39     struct driveinfo drive_info;
40     drive_info.disk = drive;
41
42     /* MBR: lba = 0, 1 sector */
43     return read_sectors(&drive_info, buf, 0, 1);
44 }
45
46 /**
47  * dev_read - read from a drive
48  * @drive:      Drive number
49  * @buf:        Pre-allocated buffer for output
50  * @lba:        Position to start reading from
51  * @sectors:    Number of sectors to read
52  *
53  * High-level routine to read from a hard drive.
54  * Return the number of sectors read on success or -1 on failure.
55  * errno_disk contains the error number.
56  **/
57 int dev_read(int drive, void *buf, unsigned int lba, int sectors)
58 {
59     struct driveinfo drive_info;
60     drive_info.disk = drive;
61
62     return read_sectors(&drive_info, buf, lba, sectors);
63 }
64
65 /**
66  * read_sectors - read several sectors from disk
67  * @drive_info:         driveinfo struct describing the disk
68  * @data:               Pre-allocated buffer for output
69  * @lba:                Position to read
70  * @sectors:            Number of sectors to read
71  *
72  * Return the number of sectors read on success or -1 on failure.
73  * errno_disk contains the error number.
74  **/
75 int read_sectors(struct driveinfo *drive_info, void *data,
76                  const unsigned int lba, const int sectors)
77 {
78     com32sys_t inreg, outreg;
79     struct ebios_dapa *dapa = __com32.cs_bounce;
80     void *buf = (char *)__com32.cs_bounce + sectors * SECTOR;
81     char *bufp = data;
82
83     if (get_drive_parameters(drive_info) == -1)
84         return -1;
85
86     memset(&inreg, 0, sizeof inreg);
87
88     if (drive_info->ebios) {
89         dapa->len = sizeof(*dapa);
90         dapa->count = sectors;
91         dapa->off = OFFS(buf);
92         dapa->seg = SEG(buf);
93         dapa->lba = lba;
94
95         inreg.esi.w[0] = OFFS(dapa);
96         inreg.ds = SEG(dapa);
97         inreg.edx.b[0] = drive_info->disk;
98         inreg.eax.b[1] = 0x42;  /* Extended read */
99     } else {
100         unsigned int c, h, s;
101
102         if (!drive_info->cbios) {       // XXX errno
103             /* We failed to get the geometry */
104             if (lba)
105                 return -1;      /* Can only read MBR */
106
107             s = 1;
108             h = 0;
109             c = 0;
110         } else
111             lba_to_chs(drive_info, lba, &s, &h, &c);
112
113         // XXX errno
114         if (s > 63 || h > 256 || c > 1023)
115             return -1;
116
117         inreg.eax.w[0] = 0x0201;        /* Read one sector */
118         inreg.ecx.b[1] = c & 0xff;
119         inreg.ecx.b[0] = s + (c >> 6);
120         inreg.edx.b[1] = h;
121         inreg.edx.b[0] = drive_info->disk;
122         inreg.ebx.w[0] = OFFS(buf);
123         inreg.es = SEG(buf);
124     }
125
126     /* Perform the read */
127     if (int13_retry(&inreg, &outreg)) {
128         errno_disk = outreg.eax.b[1];
129         return -1;              /* Give up */
130     }
131
132     memcpy(bufp, buf, sectors * SECTOR);
133
134     return sectors;
135 }