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