|
|
1.1 root 1: /******************************************************************************
2: * Copyright (c) 2011 IBM Corporation
3: * All rights reserved.
4: * This program and the accompanying materials
5: * are made available under the terms of the BSD License
6: * which accompanies this distribution, and is available at
7: * http://www.opensource.org/licenses/bsd-license.php
8: *
9: * Contributors:
10: * IBM Corporation - initial implementation
11: *****************************************************************************/
12:
13: #include <stdio.h>
14: #include <string.h>
15: #include <stdint.h>
16: #include <byteorder.h>
17:
18: #include "virtio-9p.h"
19: #include "p9.h"
20:
21:
22: /**
23: * Notes for 9P Server config:
24: *
25: * make distclean; cm make qemu
26: * sudo cp boot_rom.bin /opt/qemu/share/qemu/slof.bin
27: * /opt/qemu/bin/qemu-system-ppc64 -M pseries -m 512 -boot d -nographic -fsdev
28: * local,id=trule,path=/home/trule/virtfs,security_model=none -device
29: * virtio-9p-spapr,fsdev=trule,mount_tag=trule
30: * load virtfs:\some\file
31: */
32:
33: /* We support only one instance due to the (ab)use of globals. We
34: * use the buffer size as an open marker as well.
35: */
36: static int __buf_size;
37:
38:
39: #define ROOT_FID 1
40: #define FILE_FID 2
41: #define TAG_SIZE 128
42: #define sync() asm volatile (" sync \n" ::: "memory")
43: #define MIN(a,b) ((a)>(b)?(b):(a))
44:
45:
46: #undef DEBUG
47: //#define DEBUG
48: #ifdef DEBUG
49: #define dprintf(_x ...) printf(_x)
50: #else
51: #define dprintf(_x ...)
52: #endif
53:
54: #ifdef DEBUG
55: static void dprint_buffer(const char *name, uint8_t *buffer, int length)
56: {
57: int i;
58:
59: printf("*** %s ***", name);
60:
61: for (i = 0; i < length; i++) {
62: if (i % 16 == 0) {
63: printf("\n %04x:", i);
64: }
65:
66: printf(" %02x", buffer[i]);
67: }
68:
69: printf("\n");
70: }
71: #else
72: #define dprint_buffer(n, b, l)
73: #endif
74:
75: /**
76: * virtio_9p_transact
77: *
78: * Perform a 9P transaction over the VIRTIO queue interface. This function is
79: * registered with the p9.c library via p9_reg_transport() to provide
80: * connectivity to the 9P server.
81: *
82: * @param tx[in] Data to send, mapped to first queue item.
83: * @param tx_size[in] Size of data to send.
84: * @param rx[out] Data to receive, mappend to second queue item.
85: * @param rx_size[out] Size of data received.
86: * @return 0 = success, -ve = error.
87: */
88: static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx,
89: int *rx_size)
90: {
91: struct virtio_device *dev = opaque;
92: struct vring_desc *desc;
93: int id, i;
94: uint32_t vq_size;
95: struct vring_desc *vq_desc;
96: struct vring_avail *vq_avail;
97: struct vring_used *vq_used;
98: volatile uint16_t *current_used_idx;
99: uint16_t last_used_idx;
100:
101:
102: /* Virt IO queues. */
103: vq_size = virtio_get_qsize(dev, 0);
104: vq_desc = virtio_get_vring_desc(dev, 0);
105: vq_avail = virtio_get_vring_avail(dev, 0);
106: vq_used = virtio_get_vring_used(dev, 0);
107:
108: last_used_idx = vq_used->idx;
109: current_used_idx = &vq_used->idx;
110:
111: /* Determine descriptor index */
112: id = (vq_avail->idx * 3) % vq_size;
113:
114: /* TX in first queue item. */
115: dprint_buffer("TX", tx, tx_size);
116:
117: desc = &vq_desc[id];
118: desc->addr = (uint64_t)tx;
119: desc->len = tx_size;
120: desc->flags = VRING_DESC_F_NEXT;
121: desc->next = (id + 1) % vq_size;
122:
123: /* RX in the second queue item. */
124: desc = &vq_desc[(id + 1) % vq_size];
125: desc->addr = (uint64_t)rx;
126: desc->len = *rx_size;
127: desc->flags = VRING_DESC_F_WRITE;
128: desc->next = 0;
129:
130: /* Tell HV that the queue is ready */
131: vq_avail->ring[vq_avail->idx % vq_size] = id;
132: sync();
133: vq_avail->idx += 1;
134: virtio_queue_notify(dev, 0);
135:
136: /* Receive the response. */
137: i = 10000000;
138: while (*current_used_idx == last_used_idx && i-- > 0) {
139: // do something better
140: sync();
141: }
142: if (i == 0) {
143: return -1;
144: }
145:
146: *rx_size = MIN(*rx_size, le32_to_cpu(*(uint32_t*)(&rx[0])));
147: dprint_buffer("RX", rx, *rx_size);
148:
149: return 0;
150: }
151:
152: /**
153: * virtio_9p_init
154: *
155: * Establish the VIRTIO connection for use with the 9P server. Setup queues
156: * and negotiate capabilities. Setup the 9P (Client) library.
157: *
158: * @param reg[in] Pointer to device tree node for VIRTIO/9P interface.
159: * @param tx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
160: * @param rx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
161: * @param buf_size Somewhat redundant, buffer size expected to be 8k.
162: * @return 0 = success, -ve = error.
163: */
164: int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf,
165: int buf_size)
166: {
167: struct vring_avail *vq_avail;
168:
169: /* Check for double open */
170: if (__buf_size)
171: return -1;
172: __buf_size = buf_size;
173:
174: dprintf("%s : device at %p\n", __func__, dev->base);
175: dprintf("%s : type is %04x\n", __func__, dev->type);
176:
177: /* Reset device */
178: // XXX That will clear the virtq base. We need to move
179: // initializing it to here anyway
180: //
181: // virtio_reset_device(dev);
182:
183: /* Acknowledge device. */
184: virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE);
185:
186: /* Tell HV that we know how to drive the device. */
187: virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER);
188:
189: /* Device specific setup - we do not support special features */
190: virtio_set_guest_features(dev, 0);
191:
192: vq_avail = virtio_get_vring_avail(dev, 0);
193: vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
194: vq_avail->idx = 0;
195:
196: /* Tell HV that setup succeeded */
197: virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER
198: |VIRTIO_STAT_DRIVER_OK);
199:
200: /* Setup 9P library. */
201: p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf,
202: (uint8_t *)rx_buf);
203:
204: dprintf("%s : complete\n", __func__);
205: return 0;
206: }
207:
208: /**
209: * virtio_9p_shutdown
210: */
211: void virtio_9p_shutdown(struct virtio_device *dev)
212: {
213: /* Quiesce device */
214: virtio_set_status(dev, VIRTIO_STAT_FAILED);
215:
216: /* Reset device */
217: virtio_reset_device(dev);
218:
219: __buf_size = 0;
220: }
221:
222: /**
223: * virtio_9p_load
224: *
225: * Read a file from the 9P Server on the VIRTIO interface.
226: *
227: * @param file_name[in] File to read, use Linux style paths.
228: * @param buffer[out] Where to read the file to.
229: * @return +ve = amount of data read, -ve = error.
230: */
231: int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer)
232: {
233: int rc;
234: uint16_t tag_len;
235: char tag_name[TAG_SIZE];
236: uint64_t offset = 0;
237: uint8_t *pos = (uint8_t *)file_name;
238: int start_fid = ROOT_FID;
239: p9_connection_t connection = {
240: .message_size = __buf_size,
241: .fid = ROOT_FID,
242: .uname = "slof"
243: };
244: p9_file_t file = {
245: .connection = &connection,
246: .fid = FILE_FID,
247: };
248:
249:
250: /* Get the share name from 9P config space. */
251: tag_len = virtio_get_config(dev, 0, sizeof(tag_len));
252: if (tag_len >= TAG_SIZE)
253: tag_len = TAG_SIZE - 1;
254: __virtio_read_config(dev, tag_name, 2, tag_len);
255: connection.aname = tag_name;
256:
257: /* Connect to the 9P server. */
258: dprintf("%s : connecting, tag = %s, user = %s, msgsize = %d\n",
259: __func__, connection.aname, connection.uname,
260: connection.message_size);
261: rc = p9_version(&connection);
262: if (rc != 0) {
263: printf("Version check failed, rc = %d\n", rc);
264: goto cleanup_connection;
265: }
266: rc = p9_attach(&connection);
267: if (rc != 0) {
268: printf("Attach failed, rc = %d\n", rc);
269: goto cleanup_connection;
270: }
271: dprintf("%s : connected, msgsize = %d\n", __func__,
272: connection.message_size);
273:
274: /* Walk to the file. */
275: do {
276: dprintf("%s : walk path %s\n", __func__, pos);
277: rc = p9_walk(&connection, start_fid, FILE_FID, &pos);
278:
279: if (rc < 0) { /* Some error. */
280: printf("Walk failed, rc = %d\n", rc);
281: goto cleanup_connection;
282: }
283:
284: /*
285: * If partial walk (*pos != 0) then continue the walk from
286: * mid point with start_fid updated to current position
287: * FILE_FID. FILE_FID will then be reused for the result of
288: * the next call to walk.
289: */
290: start_fid = FILE_FID;
291: } while (*pos != 0);
292:
293: /* Open the file. */
294: dprintf("%s : stat and open\n", __func__);
295: rc = p9_stat(&file);
296: if (rc != 0) {
297: printf("Stat failed, rc = %d\n", rc);
298: goto cleanup_file;
299: }
300: rc = p9_open(&file, 0x00); /* TODO find include for "read mode" */
301: if (rc != 0) {
302: printf("Open failed, rc = %d\n", rc);
303: goto cleanup_file;
304: }
305: dprintf("%s : file opened, size %lld\n", __func__, file.length);
306:
307: /* Read the file contents to buffer. */
308: while (offset < file.length) {
309: dprintf("%s : read from offset %llu\n", __func__, offset);
310: rc = p9_read(&file, buffer + offset,
311: file.length - offset, offset);
312: dprintf("%s : read done, length was %d\n", __func__, rc);
313: if (rc < 0) {
314: printf("Read failed, rc = %d\n", rc);
315: goto cleanup_file;
316: }
317: if (rc == 0) {
318: break;
319: }
320: offset += rc;
321: rc = 0;
322: }
323:
324: /* Cleanup and disconnect. */
325: cleanup_file:
326: dprintf("%s : clunking file\n", __func__);
327: p9_clunk(&connection, file.fid);
328:
329: cleanup_connection:
330: dprintf("%s : clunking connection\n", __func__);
331: p9_clunk(&connection, connection.fid);
332:
333:
334: dprintf("%s : complete, read %llu bytes\n", __func__, offset);
335: return rc == 0 ? offset : rc;
336: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.