/[dynamips]/trunk/device.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/device.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Sat Oct 6 16:45:40 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 11560 byte(s)
make working copy

1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4 */
5
6 #define _GNU_SOURCE
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/mman.h>
14 #include <fcntl.h>
15 #include <assert.h>
16
17 #include "cpu.h"
18 #include "vm.h"
19 #include "dynamips.h"
20 #include "memory.h"
21 #include "device.h"
22
23 #define DEBUG_DEV_ACCESS 0
24
25 /* Get device by ID */
26 struct vdevice *dev_get_by_id(vm_instance_t *vm,u_int dev_id)
27 {
28 if (!vm || (dev_id >= VM_DEVICE_MAX))
29 return NULL;
30
31 return(vm->dev_array[dev_id]);
32 }
33
34 /* Get device by name */
35 struct vdevice *dev_get_by_name(vm_instance_t *vm,char *name)
36 {
37 struct vdevice *dev;
38
39 if (!vm)
40 return NULL;
41
42 for(dev=vm->dev_list;dev;dev=dev->next)
43 if (!strcmp(dev->name,name))
44 return dev;
45
46 return NULL;
47 }
48
49 /* Device lookup by physical address */
50 struct vdevice *dev_lookup(vm_instance_t *vm,m_uint64_t phys_addr,int cached)
51 {
52 struct vdevice *dev;
53
54 if (!vm)
55 return NULL;
56
57 for(dev=vm->dev_list;dev;dev=dev->next) {
58 if (cached && !(dev->flags & VDEVICE_FLAG_CACHING))
59 continue;
60
61 if ((phys_addr >= dev->phys_addr) &&
62 ((phys_addr - dev->phys_addr) < dev->phys_len))
63 return dev;
64 }
65
66 return NULL;
67 }
68
69 /* Find the next device after the specified address */
70 struct vdevice *dev_lookup_next(vm_instance_t *vm,m_uint64_t phys_addr,
71 struct vdevice *dev_start,int cached)
72 {
73 struct vdevice *dev;
74
75 if (!vm)
76 return NULL;
77
78 dev = (dev_start != NULL) ? dev_start : vm->dev_list;
79 for(;dev;dev=dev->next) {
80 if (cached && !(dev->flags & VDEVICE_FLAG_CACHING))
81 continue;
82
83 if (dev->phys_addr > phys_addr)
84 return dev;
85 }
86
87 return NULL;
88 }
89
90 /* Initialize a device */
91 void dev_init(struct vdevice *dev)
92 {
93 memset(dev,0,sizeof(*dev));
94 dev->fd = -1;
95 }
96
97 /* Allocate a device */
98 struct vdevice *dev_create(char *name)
99 {
100 struct vdevice *dev;
101
102 if (!(dev = malloc(sizeof(*dev)))) {
103 fprintf(stderr,"dev_create: insufficient memory to "
104 "create device '%s'.\n",name);
105 return NULL;
106 }
107
108 dev_init(dev);
109 dev->name = name;
110 return dev;
111 }
112
113 /* Remove a device */
114 void dev_remove(vm_instance_t *vm,struct vdevice *dev)
115 {
116 if (dev == NULL)
117 return;
118
119 vm_unbind_device(vm,dev);
120
121 vm_log(vm,"DEVICE",
122 "Removal of device %s, fd=%d, host_addr=0x%llx, flags=%d\n",
123 dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->flags);
124
125 if (dev->flags & VDEVICE_FLAG_REMAP) {
126 dev_init(dev);
127 return;
128 }
129
130 if (dev->flags & VDEVICE_FLAG_SPARSE) {
131 dev_sparse_shutdown(dev);
132
133 if (dev->flags & VDEVICE_FLAG_GHOST) {
134 vm_ghost_image_release(dev->fd);
135 dev_init(dev);
136 return;
137 }
138 }
139
140 if (dev->fd != -1) {
141 /* Unmap memory mapped file */
142 if (dev->host_addr) {
143 if (dev->flags & VDEVICE_FLAG_SYNC) {
144 msync((void *)dev->host_addr,dev->phys_len,
145 MS_SYNC|MS_INVALIDATE);
146 }
147
148 vm_log(vm,"MMAP","unmapping of device '%s', "
149 "fd=%d, host_addr=0x%llx, len=0x%x\n",
150 dev->name,dev->fd,(m_uint64_t)dev->host_addr,dev->phys_len);
151 munmap((void *)dev->host_addr,dev->phys_len);
152 }
153
154 if (dev->flags & VDEVICE_FLAG_SYNC)
155 fsync(dev->fd);
156
157 close(dev->fd);
158 } else {
159 /* Use of malloc'ed host memory: free it */
160 if (dev->host_addr)
161 free((void *)dev->host_addr);
162 }
163
164 /* reinitialize the device to a clean state */
165 dev_init(dev);
166 }
167
168 /* Show properties of a device */
169 void dev_show(struct vdevice *dev)
170 {
171 if (!dev)
172 return;
173
174 printf(" %-18s: 0x%12.12llx (0x%8.8x)\n",
175 dev->name,dev->phys_addr,dev->phys_len);
176 }
177
178 /* Show the device list */
179 void dev_show_list(vm_instance_t *vm)
180 {
181 struct vdevice *dev;
182
183 printf("\nVM \"%s\" (%u) Device list:\n",vm->name,vm->instance_id);
184
185 for(dev=vm->dev_list;dev;dev=dev->next)
186 dev_show(dev);
187
188 printf("\n");
189 }
190
191 /* device access function */
192 void *dev_access(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset,
193 u_int op_size,u_int op_type,m_uint64_t *data)
194 {
195 struct vdevice *dev = cpu->vm->dev_array[dev_id];
196
197 #if DEBUG_DEV_ACCESS
198 cpu_log(cpu,"DEV_ACCESS","%s: dev_id=%u, offset=0x%8.8x, op_size=%u, "
199 "op_type=%u, data=%p\n",dev->name,dev_id,offset,op_size,op_type,data);
200 #endif
201
202 return(dev->handler(cpu,dev,offset,op_size,op_type,data));
203 }
204
205 /* Synchronize memory for a memory-mapped (mmap) device */
206 int dev_sync(struct vdevice *dev)
207 {
208 if (!dev || !dev->host_addr)
209 return(-1);
210
211 return(msync((void *)dev->host_addr,dev->phys_len,MS_SYNC));
212 }
213
214 /* Remap a device at specified physical address */
215 struct vdevice *dev_remap(char *name,struct vdevice *orig,
216 m_uint64_t paddr,m_uint32_t len)
217 {
218 struct vdevice *dev;
219
220 if (!(dev = dev_create(name)))
221 return NULL;
222
223 dev->phys_addr = paddr;
224 dev->phys_len = len;
225 dev->flags = orig->flags | VDEVICE_FLAG_REMAP;
226 dev->fd = orig->fd;
227 dev->host_addr = orig->host_addr;
228 dev->handler = orig->handler;
229 dev->sparse_map = orig->sparse_map;
230 return dev;
231 }
232
233 /* Create a RAM device */
234 struct vdevice *dev_create_ram(vm_instance_t *vm,char *name,
235 int sparse,char *filename,
236 m_uint64_t paddr,m_uint32_t len)
237 {
238 struct vdevice *dev;
239 u_char *ram_ptr;
240
241 if (!(dev = dev_create(name)))
242 return NULL;
243
244 dev->phys_addr = paddr;
245 dev->phys_len = len;
246 dev->flags = VDEVICE_FLAG_CACHING;
247
248 if (!sparse) {
249 if (filename) {
250 dev->fd = memzone_create_file(filename,dev->phys_len,&ram_ptr);
251
252 if (dev->fd == -1) {
253 perror("dev_create_ram: mmap");
254 free(dev);
255 return NULL;
256 }
257
258 dev->host_addr = (m_iptr_t)ram_ptr;
259 } else {
260 dev->host_addr = (m_iptr_t)m_memalign(4096,dev->phys_len);
261 }
262
263 if (!dev->host_addr) {
264 free(dev);
265 return NULL;
266 }
267 } else {
268 dev_sparse_init(dev);
269 }
270
271 vm_bind_device(vm,dev);
272 return dev;
273 }
274
275 /* Create a ghosted RAM device */
276 struct vdevice *
277 dev_create_ghost_ram(vm_instance_t *vm,char *name,int sparse,char *filename,
278 m_uint64_t paddr,m_uint32_t len)
279 {
280 struct vdevice *dev;
281 u_char *ram_ptr;
282
283 if (!(dev = dev_create(name)))
284 return NULL;
285
286 dev->phys_addr = paddr;
287 dev->phys_len = len;
288 dev->flags = VDEVICE_FLAG_CACHING|VDEVICE_FLAG_GHOST;
289
290 if (!sparse) {
291 dev->fd = memzone_open_cow_file(filename,dev->phys_len,&ram_ptr);
292 if (dev->fd == -1) {
293 perror("dev_create_ghost_ram: mmap");
294 free(dev);
295 return NULL;
296 }
297
298 if (!(dev->host_addr = (m_iptr_t)ram_ptr)) {
299 free(dev);
300 return NULL;
301 }
302 } else {
303 if (vm_ghost_image_get(filename,&ram_ptr,&dev->fd) == -1) {
304 free(dev);
305 return NULL;
306 }
307
308 dev->host_addr = (m_iptr_t)ram_ptr;
309 dev_sparse_init(dev);
310 }
311
312 vm_bind_device(vm,dev);
313 return dev;
314 }
315
316 /* Create a memory alias */
317 struct vdevice *dev_create_ram_alias(vm_instance_t *vm,char *name,char *orig,
318 m_uint64_t paddr,m_uint32_t len)
319 {
320 struct vdevice *dev,*orig_dev;
321
322 /* try to locate the device */
323 if (!(orig_dev = dev_get_by_name(vm,orig))) {
324 fprintf(stderr,"VM%u: dev_create_ram_alias: unknown device '%s'.\n",
325 vm->instance_id,orig);
326 return NULL;
327 }
328
329 if (!(dev = dev_remap(name,orig_dev,paddr,len))) {
330 fprintf(stderr,"VM%u: dev_create_ram_alias: unable to create "
331 "new device %s.\n",vm->instance_id,name);
332 return NULL;
333 }
334
335 vm_bind_device(vm,dev);
336 return dev;
337 }
338
339 /* Initialize a sparse device */
340 int dev_sparse_init(struct vdevice *dev)
341 {
342 u_int i,nr_pages;
343 size_t len;
344
345 /* create the sparse mapping */
346 nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT);
347 len = nr_pages * sizeof(m_iptr_t);
348
349 if (!(dev->sparse_map = malloc(len)))
350 return(-1);
351
352 if (!dev->host_addr) {
353 memset(dev->sparse_map,0,len);
354 } else {
355 for(i=0;i<nr_pages;i++)
356 dev->sparse_map[i] = dev->host_addr + (i << VM_PAGE_SHIFT);
357 }
358
359 dev->flags |= VDEVICE_FLAG_SPARSE;
360 return(0);
361 }
362
363 /* Shutdown sparse device structures */
364 int dev_sparse_shutdown(struct vdevice *dev)
365 {
366 if (!(dev->flags & VDEVICE_FLAG_SPARSE))
367 return(-1);
368
369 free(dev->sparse_map);
370 dev->sparse_map = NULL;
371 return(0);
372 }
373
374 /* Show info about a sparse device */
375 int dev_sparse_show_info(struct vdevice *dev)
376 {
377 u_int i,nr_pages,dirty_pages;
378
379 printf("Sparse information for device '%s':\n",dev->name);
380
381 if (!(dev->flags & VDEVICE_FLAG_SPARSE)) {
382 printf("This is not a sparse device.\n");
383 return(-1);
384 }
385
386 if (!dev->sparse_map) {
387 printf("No sparse map.\n");
388 return(-1);
389 }
390
391 nr_pages = normalize_size(dev->phys_len,VM_PAGE_SIZE,VM_PAGE_SHIFT);
392 dirty_pages = 0;
393
394 for(i=0;i<nr_pages;i++)
395 if (dev->sparse_map[i] & VDEVICE_PTE_DIRTY)
396 dirty_pages++;
397
398 printf("%u dirty pages on a total of %u pages.\n",dirty_pages,nr_pages);
399 return(0);
400 }
401
402 /* Get an host address for a sparse device */
403 m_iptr_t dev_sparse_get_host_addr(vm_instance_t *vm,struct vdevice *dev,
404 m_uint64_t paddr,u_int op_type,int *cow)
405 {
406 m_iptr_t ptr,ptr_new;
407 u_int offset;
408
409 offset = (paddr - dev->phys_addr) >> VM_PAGE_SHIFT;
410 ptr = dev->sparse_map[offset];
411 *cow = 0;
412
413 /*
414 * If the device is not in COW mode, allocate a host page if the physical
415 * page is requested for the first time.
416 */
417 if (!dev->host_addr) {
418 if (!(ptr & VDEVICE_PTE_DIRTY)) {
419 ptr = (m_iptr_t)vm_alloc_host_page(vm);
420 assert(ptr);
421
422 dev->sparse_map[offset] = ptr | VDEVICE_PTE_DIRTY;
423 return(ptr);
424 }
425
426 return(ptr & VM_PAGE_MASK);
427 }
428
429 /*
430 * We have a "ghost" base. We apply the copy-on-write (COW) mechanism
431 * ourselves.
432 */
433 if (ptr & VDEVICE_PTE_DIRTY)
434 return(ptr & VM_PAGE_MASK);
435
436 if (op_type == MTS_READ) {
437 *cow = 1;
438 return(ptr & VM_PAGE_MASK);
439 }
440
441 /* Write attempt on a "ghost" page. Duplicate it */
442 ptr_new = (m_iptr_t)vm_alloc_host_page(vm);
443 assert(ptr_new);
444
445 memcpy((void *)ptr_new,(void *)(ptr & VM_PAGE_MASK),VM_PAGE_SIZE);
446 dev->sparse_map[offset] = ptr_new | VDEVICE_PTE_DIRTY;
447 return(ptr_new);
448 }
449
450 /* dummy console handler */
451 static void *dummy_console_handler(cpu_gen_t *cpu,struct vdevice *dev,
452 m_uint32_t offset,u_int op_size,
453 u_int op_type,m_uint64_t *data)
454 {
455 switch(offset) {
456 case 0x40c:
457 if (op_type == MTS_READ)
458 *data = 0x04; /* tx ready */
459 break;
460
461 case 0x41c:
462 if (op_type == MTS_WRITE) {
463 printf("%c",(u_char)(*data & 0xff));
464 fflush(stdout);
465 }
466 break;
467 }
468
469 return NULL;
470 }
471
472 /* Create a dummy console */
473 int dev_create_dummy_console(vm_instance_t *vm)
474 {
475 struct vdevice *dev;
476
477 if (!(dev = dev_create("dummy_console")))
478 return(-1);
479
480 dev->phys_addr = 0x1e840000; /* 0x1f000000; */
481 dev->phys_len = 4096;
482 dev->handler = dummy_console_handler;
483
484 vm_bind_device(vm,dev);
485 return(0);
486 }

  ViewVC Help
Powered by ViewVC 1.1.26