Ver código fonte

Added (untested) support for ATA devices. This code should be generic

enough to support both real IDE chipsets and AoE.
tags/v0.9.3
Michael Brown 18 anos atrás
pai
commit
80958ff69c
2 arquivos alterados com 362 adições e 0 exclusões
  1. 169
    0
      src/drivers/block/ata.c
  2. 193
    0
      src/include/gpxe/ata.h

+ 169
- 0
src/drivers/block/ata.c Ver arquivo

@@ -0,0 +1,169 @@
1
+/*
2
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stddef.h>
20
+#include <string.h>
21
+#include <byteswap.h>
22
+#include <gpxe/blockdev.h>
23
+#include <gpxe/ata.h>
24
+
25
+/** @file
26
+ *
27
+ * ATA block device
28
+ *
29
+ */
30
+
31
+static inline __attribute__ (( always_inline )) struct ata_device *
32
+block_to_ata ( struct block_device *blockdev ) {
33
+	return container_of ( blockdev, struct ata_device, blockdev );
34
+}
35
+
36
+/**
37
+ * Issue ATA command
38
+ *
39
+ * @v ata		ATA device
40
+ * @v command		ATA command
41
+ * @ret rc		Return status code
42
+ */
43
+static inline __attribute__ (( always_inline )) int
44
+ata_command ( struct ata_device *ata, struct ata_command *command ) {
45
+	DBG ( "ATA cmd %02x dev %02x fl %02x LBA %llx count %04x\n",
46
+	      command.cb.cmd_stat, command.cb.device, command.cb.flags,
47
+	      ( unsigned long long ) command.cb.lba.native,
48
+	      command.cb.count.native );
49
+
50
+	return ata->command ( ata, command );	
51
+}
52
+
53
+/**
54
+ * Read block from / write block to ATA device
55
+ *
56
+ * @v write		Write flag (ATA_FL_WRITE or 0)
57
+ * @v blockdev		Block device
58
+ * @v block		LBA block number
59
+ * @v count		Block count
60
+ * @v buffer		Data buffer
61
+ * @ret rc		Return status code
62
+ */
63
+static __attribute__ (( regparm ( 1 ) )) int
64
+ata_rw ( int write, struct block_device *blockdev, uint64_t block,
65
+	 unsigned long count, userptr_t buffer ) {
66
+	struct ata_device *ata = block_to_ata ( blockdev );
67
+	struct ata_command command;
68
+	int lba48 = ( ata->flags & ATA_FL_LBA48 );
69
+
70
+	memset ( &command, 0, sizeof ( command ) );
71
+	command.cb.lba.native = block;
72
+	command.cb.count.native = count;
73
+	command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
74
+	command.cb.flags = ( ata->flags | write );
75
+	command.cb.cmd_stat = ( write ? ATA_CMD_WRITE : ATA_CMD_READ );
76
+	if ( lba48 ) {
77
+		command.cb.cmd_stat |= ATA_CMD_EXT;
78
+	} else {
79
+		command.cb.device |= command.cb.lba.bytes.low_prev;
80
+	}
81
+	command.data = buffer;
82
+	command.data_len = ( count * blockdev->blksize );
83
+	return ata_command ( ata, &command );
84
+}
85
+
86
+/**
87
+ * Read block from ATA device
88
+ *
89
+ * @v blockdev		Block device
90
+ * @v block		LBA block number
91
+ * @v count		Block count
92
+ * @v buffer		Data buffer
93
+ * @ret rc		Return status code
94
+ */
95
+static int ata_read ( struct block_device *blockdev, uint64_t block,
96
+		      unsigned long count, userptr_t buffer ) {
97
+	/* Pass through to ata_rw().  Since ata_rw is regparm(1), this
98
+	 * is extremely efficient; just a mov and a jmp.
99
+	 */
100
+	return ata_rw ( 0, blockdev, block, count, buffer );
101
+}
102
+
103
+/**
104
+ * Write block to ATA device
105
+ *
106
+ * @v blockdev		Block device
107
+ * @v block		LBA block number
108
+ * @v count		Block count
109
+ * @v buffer		Data buffer
110
+ * @ret rc		Return status code
111
+ */
112
+static int ata_write ( struct block_device *blockdev, uint64_t block,
113
+		       unsigned long count, userptr_t buffer ) {
114
+	/* Pass through to ata_rw().  Since ata_rw is regparm(1), this
115
+	 * is extremely efficient; just a mov and a jmp.
116
+	 */
117
+	return ata_rw ( ATA_FL_WRITE, blockdev, block, count, buffer );
118
+}
119
+
120
+/**
121
+ * Identify ATA device
122
+ *
123
+ * @v blockdev		Block device
124
+ * @ret rc		Return status code
125
+ */
126
+static int ata_identify ( struct block_device *blockdev ) {
127
+	struct ata_device *ata = block_to_ata ( blockdev );
128
+	struct ata_command command;
129
+	struct ata_identity identity;
130
+	int rc;
131
+
132
+	/* Issue IDENTIFY */
133
+	memset ( &command, 0, sizeof ( command ) );
134
+	command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
135
+	command.cb.cmd_stat = ATA_CMD_IDENTIFY;
136
+	command.data = virt_to_user ( &identity );
137
+	command.data_len = sizeof ( identity );
138
+	if ( ( rc = ata_command ( ata, &command ) ) != 0 )
139
+		return rc;
140
+
141
+	/* Fill in block device parameters */
142
+	blockdev->blksize = ATA_SECTOR_SIZE;
143
+	if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
144
+		ata->flags |= ATA_FL_LBA48;
145
+		blockdev->blocks = le64_to_cpu ( identity.lba48_sectors );
146
+	} else {
147
+		blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
148
+	}
149
+	return 0;
150
+}
151
+
152
+/**
153
+ * Initialise ATA device
154
+ *
155
+ * @v ata		ATA device
156
+ * @ret rc		Return status code
157
+ *
158
+ * Initialises an ATA device.  The ata_device::command field and the
159
+ * @c ATA_FL_SLAVE portion of the ata_device::flags field must already
160
+ * be filled in.  This function will configure ata_device::blockdev,
161
+ * including issuing an IDENTIFY DEVICE call to determine the block
162
+ * size and total device size.
163
+ */
164
+int init_atadev ( struct ata_device *ata ) {
165
+	/** Fill in read and write methods, and get device capacity */
166
+	ata->blockdev.read = ata_read;
167
+	ata->blockdev.write = ata_write;
168
+	return ata_identify ( &ata->blockdev );
169
+}

+ 193
- 0
src/include/gpxe/ata.h Ver arquivo

@@ -0,0 +1,193 @@
1
+#ifndef _GPXE_ATA_H
2
+#define _GPXE_ATA_H
3
+
4
+#include <stdint.h>
5
+#include <gpxe/blockdev.h>
6
+#include <gpxe/uaccess.h>
7
+
8
+/** @file
9
+ *
10
+ * ATA devices
11
+ *
12
+ */
13
+
14
+/**
15
+ * An ATA Logical Block Address
16
+ *
17
+ * ATA controllers have three byte-wide registers for specifying the
18
+ * block address: LBA Low, LBA Mid and LBA High.  This allows for a
19
+ * 24-bit address.  Some devices support the "48-bit address feature
20
+ * set" (LBA48), in which case each of these byte-wide registers is
21
+ * actually a two-entry FIFO, and the "previous" byte pushed into the
22
+ * FIFO is used as the corresponding high-order byte.  So, to set up
23
+ * the 48-bit address 0x12345678abcd, you would issue
24
+ *
25
+ *     0x56 -> LBA Low register
26
+ *     0xcd -> LBA Low register
27
+ *     0x34 -> LBA Mid register
28
+ *     0xab -> LBA Mid register
29
+ *     0x12 -> LBA High register
30
+ *     0x78 -> LBA High register
31
+ *
32
+ * This structure encapsulates this information by providing a single
33
+ * 64-bit integer in native byte order, unioned with bytes named so
34
+ * that the sequence becomes
35
+ *
36
+ *     low_prev  -> LBA Low register
37
+ *     low_cur   -> LBA Low register
38
+ *     mid_prev  -> LBA Mid register
39
+ *     mid_cur   -> LBA Mid register
40
+ *     high_prev -> LBA High register
41
+ *     high_cur  -> LBA High register
42
+ *
43
+ * Just to complicate matters further, in non-LBA48 mode it is
44
+ * possible to have a 28-bit address, in which case bits 27:24 must be
45
+ * written into the low four bits of the Device register.
46
+ */
47
+union ata_lba {
48
+	/** LBA as a 64-bit integer in native-endian order */
49
+	uint64_t native;
50
+	/** ATA registers */
51
+	struct {
52
+#if __BYTE_ORDER == __LITTLE_ENDIAN
53
+		uint8_t low_cur;
54
+		uint8_t mid_cur;
55
+		uint8_t high_cur;
56
+		uint8_t low_prev;
57
+		uint8_t mid_prev;
58
+		uint8_t high_prev;
59
+		uint8_t pad[2];
60
+#elif __BYTE_ORDER == __BIG_ENDIAN
61
+		uint8_t pad[2];
62
+		uint8_t high_prev;
63
+		uint8_t mid_prev;
64
+		uint8_t low_prev;
65
+		uint8_t high_cur;
66
+		uint8_t mid_cur;
67
+		uint8_t low_cur;
68
+#else
69
+#error "I need a byte order"
70
+#endif
71
+	} bytes;
72
+};
73
+
74
+/** An ATA 2-byte FIFO register */
75
+union ata_fifo {
76
+	/** Value in native-endian order */
77
+	uint16_t native;
78
+	/** ATA registers */
79
+	struct {
80
+#if __BYTE_ORDER == __LITTLE_ENDIAN
81
+		uint8_t cur;
82
+		uint8_t prev;
83
+#elif __BYTE_ORDER == __BIG_ENDIAN
84
+		uint8_t prev;
85
+		uint8_t cur;
86
+#else
87
+#error "I need a byte order"
88
+#endif
89
+	} bytes;
90
+};
91
+
92
+/** ATA command block */
93
+struct ata_cb {
94
+	/** Logical block address */
95
+	union ata_lba lba;
96
+	/** Sector count */
97
+	union ata_fifo count;
98
+	/** Error/feature register */
99
+	union ata_fifo err_feat;
100
+	/** Device register */
101
+	uint8_t device;
102
+	/** Command/status register */
103
+	uint8_t cmd_stat;
104
+	/** Flags
105
+	 *
106
+	 * This field does not correspond to any ATA register.
107
+	 */
108
+	uint8_t flags;
109
+};
110
+
111
+/** LBA48 extended addressing */
112
+#define ATA_FL_LBA48 0x40
113
+
114
+/** Device 1 ("slave") */
115
+#define ATA_FL_SLAVE 0x10
116
+
117
+/** Write command */
118
+#define ATA_FL_WRITE 0x01
119
+
120
+/** Obsolete bits in the ATA device register */
121
+#define ATA_DEV_OBSOLETE 0xa0
122
+
123
+/** LBA flag in the ATA device register */
124
+#define ATA_DEV_LBA 0x40
125
+
126
+/** "Read sectors" command */
127
+#define ATA_CMD_READ 0x20
128
+
129
+/** "Write sectors" command */
130
+#define ATA_CMD_WRITE 0x30
131
+
132
+/** "Identify" command */
133
+#define ATA_CMD_IDENTIFY 0xec
134
+
135
+/** "Extended (LBA48)" command modifier
136
+ *
137
+ * This doesn't apply to all ATA commands, but it does for @c
138
+ * ATA_CMD_READ and @c ATA_CMD_WRITE.
139
+ */
140
+#define ATA_CMD_EXT 0x04
141
+
142
+/** An ATA command */
143
+struct ata_command {
144
+	/** ATA command block */
145
+	struct ata_cb cb;
146
+	/** Data buffer */
147
+	userptr_t data;
148
+	/** Data buffer length */
149
+	size_t data_len;
150
+};
151
+
152
+/**
153
+ * Structure returned by ATA IDENTIFY command
154
+ *
155
+ * This is a huge structure with many fields that we don't care about,
156
+ * so we implement only a few fields.
157
+ */
158
+struct ata_identity {
159
+	uint16_t ignore_a[60]; /* words 0-59 */
160
+	uint32_t lba_sectors; /* words 60-61 */
161
+	uint16_t ignore_b[21]; /* words 62-82 */
162
+	uint16_t supports_lba48; /* word 83 */
163
+	uint16_t ignore_c[16]; /* words 84-99 */
164
+	uint64_t lba48_sectors; /* words 100-103 */
165
+	uint16_t ignore_d[152]; /* words 104-255 */
166
+};
167
+
168
+/** Supports LBA48 flag */
169
+#define ATA_SUPPORTS_LBA48 ( 1 << 10 )
170
+
171
+/** ATA sector size */
172
+#define ATA_SECTOR_SIZE 512
173
+
174
+/** An ATA device */
175
+struct ata_device {
176
+	/** Block device interface */
177
+	struct block_device blockdev;
178
+	/** Flags */
179
+	int flags;
180
+	/**
181
+	 * Issue ATA command
182
+	 *
183
+	 * @v ata		ATA device
184
+	 * @v command		ATA command
185
+	 * @ret rc		Return status code
186
+	 */
187
+	int ( * command ) ( struct ata_device *ata,
188
+			    struct ata_command *command );
189
+};
190
+
191
+extern int init_atadev ( struct ata_device *ata );
192
+
193
+#endif /* _GPXE_ATA_H */

Carregando…
Cancelar
Salvar