2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20 #include <palacios/vmm.h>
21 #include <devices/ide.h>
22 #include "ide-types.h"
26 #define PRI_DATA_PORT 0x1f0
27 #define PRI_FEATURES_PORT 0x1f1
28 #define PRI_SECT_CNT_PORT 0x1f2
29 #define PRI_SECT_NUM_PORT 0x1f3
30 #define PRI_CYL_LOW_PORT 0x1f4
31 #define PRI_CYL_HIGH_PORT 0x1f5
32 #define PRI_DRV_SEL_PORT 0x1f6
33 #define PRI_CMD_PORT 0x1f7
34 #define PRI_CTRL_PORT 0x3f6
35 #define PRI_ADDR_REG_PORT 0x3f7
37 #define SEC_DATA_PORT 0x170
38 #define SEC_FEATURES_PORT 0x171
39 #define SEC_SECT_CNT_PORT 0x172
40 #define SEC_SECT_NUM_PORT 0x173
41 #define SEC_CYL_LOW_PORT 0x174
42 #define SEC_CYL_HIGH_PORT 0x175
43 #define SEC_DRV_SEL_PORT 0x176
44 #define SEC_CMD_PORT 0x177
45 #define SEC_CTRL_PORT 0x376
46 #define SEC_ADDR_REG_PORT 0x377
49 typedef enum {IDE_DISK, IDE_CDROM, IDE_NONE} ide_dev_type_t;
50 static const char * ide_dev_type_strs[] = {"HARDDISK", "CDROM", "NONE"};
53 static inline const char * device_type_to_str(ide_dev_type_t type) {
58 return ide_dev_type_strs[type];
67 uint8_t data_port; // 0x1f0,0x170
68 struct ide_error_reg error_reg; // [read] 0x1f1,0x171
69 uint8_t sector_count; // 0x1f2,0x172
70 uint8_t sector_num; // 0x1f3,0x173
75 uint8_t cylinder_low; // 0x1f4,0x174
76 uint8_t cylinder_high; // 0x1f5,0x175
77 } __attribute__((packed));
78 } __attribute__((packed));
80 struct ide_drive_head_reg drive_head; // 0x1f6,0x176
82 struct ide_status_reg status; // [read] 0x1f7,0x177
83 uint8_t command_reg; // [write] 0x1f7,0x177
87 struct ide_drive_ctrl_reg ctrl_reg; // [write] 0x3f6,0x376
91 ide_dev_type_t drive_type;
98 struct ide_drive drives[2];
105 struct ide_internal {
106 struct ide_channel channels[2];
112 static inline int get_channel_index(ushort_t port) {
113 if (((port & 0xfff8) == 0x1f0) ||
114 ((port & 0xfffe) == 0x3f6)) {
116 } else if (((port & 0xfff8) == 0x170) ||
117 ((port & 0xfffe) == 0x376)) {
124 static inline struct ide_channel * get_selected_channel(struct ide_internal * ide, ushort_t port) {
125 int channel_idx = get_channel_index(port);
126 return &(ide->channels[channel_idx]);
129 static inline struct ide_drive * get_selected_drive(struct ide_channel * channel) {
130 return &(channel->drives[channel->drive_sel]);
135 static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
136 PrintDebug("IDE: Writing Command Port %x (val=%x)\n", port, *(uint8_t *)src);
141 static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
142 PrintDebug("IDE: Writing Data Port %x (val=%x)\n", port, *(uint8_t *)src);
147 static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
148 PrintDebug("IDE: Reading Data Port %x\n", port);
152 static int write_port_std(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
153 PrintDebug("IDE: Writing Standard Port %x (val=%x)\n", port, *(uint8_t *)src);
158 static int read_port_std(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
159 struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
160 struct ide_channel * channel = get_selected_channel(ide, port);
161 struct ide_drive * drive = get_selected_drive(channel);
164 PrintError("Invalid Read length on IDE port %x\n", port);
168 PrintDebug("IDE: Reading Standard Port %x\n", port);
171 if ((port == PRI_ADDR_REG_PORT) ||
172 (port == SEC_ADDR_REG_PORT)) {
173 // unused, return 0xff
174 *(uint8_t *)dst = 0xff;
179 // if no drive is present just return 0 + reserved bits
180 if (drive->drive_type == IDE_NONE) {
181 if ((port == PRI_DRV_SEL_PORT) ||
182 (port == SEC_DRV_SEL_PORT)) {
183 *(uint8_t *)dst = 0xa0;
193 // This is really the error register.
194 case PRI_FEATURES_PORT:
195 case SEC_FEATURES_PORT:
196 *(uint8_t *)dst = drive->error_reg.val;
199 case PRI_SECT_CNT_PORT:
200 case SEC_SECT_CNT_PORT:
201 *(uint8_t *)dst = drive->sector_count;
204 case PRI_SECT_NUM_PORT:
205 case SEC_SECT_NUM_PORT:
206 *(uint8_t *)dst = drive->sector_num;
209 case PRI_CYL_LOW_PORT:
210 case SEC_CYL_LOW_PORT:
211 *(uint8_t *)dst = drive->cylinder_low;
215 case PRI_CYL_HIGH_PORT:
216 case SEC_CYL_HIGH_PORT:
217 *(uint8_t *)dst = drive->cylinder_high;
220 case PRI_DRV_SEL_PORT:
221 case SEC_DRV_SEL_PORT: // hard disk drive and head register 0x1f6
222 *(uint8_t *)dst = drive->drive_head.val;
229 // Something about lowering interrupts here....
230 *(uint8_t *)dst = drive->status.val;
234 PrintError("Invalid Port: %x\n", port);
243 static void init_drive(struct ide_drive * drive) {
244 drive->data_port = 0x00;
245 drive->error_reg.val = 0x01;
246 drive->sector_count = 0x01;
247 drive->sector_num = 0x01;
248 drive->cylinder = 0x0000;
249 drive->drive_head.val = 0x00;
251 drive->status.val = 0x00;
252 drive->command_reg = 0x00;
254 drive->ctrl_reg.val = 0x08;
256 drive->drive_type = IDE_NONE;
260 static void init_channel(struct ide_channel * channel) {
263 for (i = 0; i < 2; i++) {
264 init_drive(&(channel->drives[i]));
267 channel->drive_sel = 0;
270 static void init_ide_state(struct ide_internal * ide) {
273 for (i = 0; i < 2; i++) {
274 init_channel(&(ide->channels[i]));
280 static int init_ide(struct vm_device * dev) {
281 struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
283 PrintDebug("IDE: Initializing IDE\n");
288 v3_dev_hook_io(dev, PRI_CTRL_PORT,
289 &read_port_std, &write_port_std);
291 v3_dev_hook_io(dev, PRI_DATA_PORT,
292 &read_data_port, &write_data_port);
293 v3_dev_hook_io(dev, PRI_FEATURES_PORT,
294 &read_port_std, &write_port_std);
295 v3_dev_hook_io(dev, PRI_SECT_CNT_PORT,
296 &read_port_std, &write_port_std);
297 v3_dev_hook_io(dev, PRI_SECT_NUM_PORT,
298 &read_port_std, &write_port_std);
299 v3_dev_hook_io(dev, PRI_CYL_LOW_PORT,
300 &read_port_std, &write_port_std);
301 v3_dev_hook_io(dev, PRI_CYL_HIGH_PORT,
302 &read_port_std, &write_port_std);
303 v3_dev_hook_io(dev, PRI_DRV_SEL_PORT,
304 &read_port_std, &write_port_std);
305 v3_dev_hook_io(dev, PRI_CMD_PORT,
306 &read_port_std, &write_cmd_port);
309 v3_dev_hook_io(dev, SEC_CTRL_PORT,
310 &read_port_std, &write_port_std);
312 v3_dev_hook_io(dev, SEC_DATA_PORT,
313 &read_data_port, &write_data_port);
314 v3_dev_hook_io(dev, SEC_FEATURES_PORT,
315 &read_port_std, &write_port_std);
316 v3_dev_hook_io(dev, SEC_SECT_CNT_PORT,
317 &read_port_std, &write_port_std);
318 v3_dev_hook_io(dev, SEC_SECT_NUM_PORT,
319 &read_port_std, &write_port_std);
320 v3_dev_hook_io(dev, SEC_CYL_LOW_PORT,
321 &read_port_std, &write_port_std);
322 v3_dev_hook_io(dev, SEC_CYL_HIGH_PORT,
323 &read_port_std, &write_port_std);
324 v3_dev_hook_io(dev, SEC_DRV_SEL_PORT,
325 &read_port_std, &write_port_std);
326 v3_dev_hook_io(dev, SEC_CMD_PORT,
327 &read_port_std, &write_cmd_port);
331 v3_dev_hook_io(dev, SEC_ADDR_REG_PORT,
332 &read_port_std, &write_port_std);
334 v3_dev_hook_io(dev, PRI_ADDR_REG_PORT,
335 &read_port_std, &write_port_std);
341 static int deinit_ide(struct vm_device * dev) {
342 // unhook io ports....
343 // deregister from PCI?
348 static struct vm_device_ops dev_ops = {
350 .deinit = deinit_ide,
357 struct vm_device * v3_create_ide() {
358 struct ide_internal * ide = (struct ide_internal *)V3_Malloc(sizeof(struct ide_internal));
359 struct vm_device * device = v3_create_device("IDE", &dev_ops, ide);
363 PrintDebug("IDE: Creating IDE bus x 2\n");