Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


added network harddisk support
[palacios.git] / palacios / src / devices / net_cd.c
1 /* 
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.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
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.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <devices/net_cd.h>
22 #include <devices/ide.h>
23 #include <palacios/vmm_socket.h>
24
25 #ifndef DEBUG_IDE
26 #undef PrintDebug
27 #define PrintDebug(fmt, args...)
28 #endif 
29
30
31 #define NBD_READ_CMD 0x1
32 #define NBD_WRITE_CMD 0x2
33 #define NBD_CAPACITY_CMD 0x3
34
35 #define NBD_STATUS_OK 0x00
36 #define NBD_STATUS_ERR 0xff
37
38 struct cd_state {
39     uint64_t capacity; // in bytes
40
41     int socket;
42
43     uint32_t ip_addr;
44     uint16_t port;
45
46     char disk_name[32];
47
48     struct vm_device * ide;
49
50     uint_t bus;
51     uint_t drive;
52 };
53
54
55 static int send_all(int socket, char * buf, int length) {
56     int bytes_sent = 0;
57     
58     PrintDebug("Sending %d bytes\n", length - bytes_sent);
59     while (bytes_sent < length) {
60         int tmp_bytes = V3_Send(socket, buf + bytes_sent, length - bytes_sent);
61         PrintDebug("Sent %d bytes\n", tmp_bytes);
62         
63         if (tmp_bytes == 0) {
64             PrintError("Connection Closed unexpectedly\n");
65             return -1;
66         }
67         
68         bytes_sent += tmp_bytes;
69     }
70     
71     return 0;
72 }
73
74
75 static int recv_all(int socket, char * buf, int length) {
76     int bytes_read = 0;
77     
78     PrintDebug("Reading %d bytes\n", length - bytes_read);
79     while (bytes_read < length) {
80         int tmp_bytes = V3_Recv(socket, buf + bytes_read, length - bytes_read);
81         PrintDebug("Received %d bytes\n", tmp_bytes);
82         
83         if (tmp_bytes == 0) {
84             PrintError("Connection Closed unexpectedly\n");
85             return -1;
86         }
87         
88         bytes_read += tmp_bytes;
89     }
90     
91     return 0;
92 }
93
94 // CDs always read 2048 byte blocks... ?
95 static int cd_read(uint8_t * buf, int block_count, uint64_t lba,  void * private_data) {
96     struct vm_device * cd_dev = (struct vm_device *)private_data;
97     struct cd_state * cd = (struct cd_state *)(cd_dev->private_data);
98     uint64_t offset = lba * ATAPI_BLOCK_SIZE;
99     int length = block_count * ATAPI_BLOCK_SIZE;
100     uint8_t status;
101     uint32_t ret_len = 0;
102     char nbd_cmd[4] = {0,0,0,0};
103
104     nbd_cmd[0] = NBD_READ_CMD;
105     
106     if (send_all(cd->socket, nbd_cmd, 4) == -1) {
107         PrintError("Error sending capacity command\n");
108         return -1;
109     }
110     
111     if (send_all(cd->socket, (char *)&offset, 8) == -1) {
112         PrintError("Error sending read offset\n");
113         return -1;
114     }
115
116     if (send_all(cd->socket, (char *)&length, 4) == -1) {
117         PrintError("Error sending read length\n");
118         return -1;
119     }
120
121     if (recv_all(cd->socket, (char *)&status, 1) == -1) {
122         PrintError("Error receiving status\n");
123         return -1;
124     }
125
126     if (status != NBD_STATUS_OK) {
127         PrintError("NBD Error....\n");
128         return -1;
129     }
130
131     PrintDebug("Reading Data Ret Length\n");
132
133     if (recv_all(cd->socket, (char *)&ret_len, 4) == -1) {
134         PrintError("Error receiving Return read length\n");
135         return -1;
136     }
137
138     if (ret_len != length) {
139         PrintError("Read length mismatch (req=%d) (result=%d)\n", length, ret_len);
140         return -1;
141     }
142
143     PrintDebug("Reading Data (%d bytes)\n", ret_len);
144
145     if (recv_all(cd->socket, (char *)buf, ret_len) == -1) {
146         PrintError("Read Data Error\n");
147         return -1;
148     }
149     
150     return 0;
151 }
152
153
154 static uint32_t cd_get_capacity(void * private_data) {
155     struct vm_device * cd_dev = (struct vm_device *)private_data;
156     struct cd_state * cd = (struct cd_state *)(cd_dev->private_data);
157
158     return cd->capacity / ATAPI_BLOCK_SIZE;
159 }
160
161 static struct v3_ide_cd_ops cd_ops = {
162     .read = cd_read, 
163     .get_capacity = cd_get_capacity,
164 };
165
166
167 static int cd_init(struct vm_device * dev) {
168     struct cd_state * cd = (struct cd_state *)(dev->private_data);
169     char header[64];
170     
171     PrintDebug("Intializing Net CD\n");
172
173     cd->socket = V3_Create_TCP_Socket();
174
175     PrintDebug("CD socket: %d\n", cd->socket);
176     PrintDebug("Connecting to: %s:%d\n", v3_inet_ntoa(cd->ip_addr), cd->port);
177
178     V3_Connect_To_IP(cd->socket, v3_ntohl(cd->ip_addr), cd->port);
179
180
181     PrintDebug("Connected to NBD server\n");
182
183     //snprintf(header, 64, "V3_NBD_1 %s\n", cd->disk_name);
184     strcpy(header, "V3_NBD_1 ");
185     strncat(header, cd->disk_name, 32);
186     strncat(header, "\n", 1);
187
188
189     if (send_all(cd->socket, header, strlen(header)) == -1) {
190         PrintError("Error connecting to Network Block Device: %s\n", cd->disk_name);
191         return -1;
192     }
193
194     // Cache Capacity
195     {
196         char nbd_cmd[4] = {0,0,0,0};
197
198         nbd_cmd[0] = NBD_CAPACITY_CMD;
199         
200         if (send_all(cd->socket, nbd_cmd, 4) == -1) {
201             PrintError("Error sending capacity command\n");
202             return -1;
203         }
204
205         if (recv_all(cd->socket, (char *)&(cd->capacity), 8) == -1) {
206             PrintError("Error Receiving Capacity\n");
207             return -1;
208         }       
209
210         PrintDebug("Capacity: %p\n", (void *)(cd->capacity));
211     }
212
213
214     PrintDebug("Registering CD\n");
215
216     if (v3_ide_register_cdrom(cd->ide, cd->bus, cd->drive, "NET-CD", &cd_ops, dev) == -1) {
217         return -1;
218     }
219
220     PrintDebug("intialization done\n");
221     
222     return 0;
223 }
224
225
226 static int cd_deinit(struct vm_device * dev) {
227     return 0;
228 }
229
230 static struct vm_device_ops dev_ops = {
231     .init = cd_init, 
232     .deinit = cd_deinit,
233     .reset = NULL,
234     .start = NULL,
235     .stop = NULL,
236 };
237
238 struct vm_device * v3_create_net_cd(struct vm_device * ide, 
239                                     uint_t bus, uint_t drive, 
240                                     const char * ip_str, uint16_t port, 
241                                     const char * disk_tag) {
242     struct cd_state * cd = (struct cd_state *)V3_Malloc(sizeof(struct cd_state));
243
244     PrintDebug("Registering Net CD at %s:%d disk=%s\n", ip_str, port, disk_tag);
245
246     strncpy(cd->disk_name, disk_tag, sizeof(cd->disk_name));
247     cd->ip_addr = v3_inet_addr(ip_str);
248     cd->port = port;
249
250     cd->ide = ide;
251     cd->bus = bus;
252     cd->drive = drive;
253         
254     struct vm_device * cd_dev = v3_create_device("NET-CD", &dev_ops, cd);
255
256     return cd_dev;
257 }