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.


updated devices to use new host event interface
[palacios.git] / palacios / src / devices / nvram.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, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Peter Dinda <pdinda@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
21 #include <devices/nvram.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_types.h>
24
25
26 #ifndef DEBUG_NVRAM
27 #undef PrintDebug
28 #define PrintDebug(fmt, args...)
29 #endif
30
31
32 #define NVRAM_REG_PORT  0x70
33 #define NVRAM_DATA_PORT 0x71
34
35 #define NVRAM_RTC_IRQ   0x8
36
37
38 typedef enum {NVRAM_READY, NVRAM_REG_POSTED} nvram_state_t;
39
40
41 #define NVRAM_REG_MAX   256
42
43
44 // These are borrowed from Bochs, which borrowed from
45 // Ralf Brown's interupt list, and extended
46 #define NVRAM_REG_SEC                     0x00
47 #define NVRAM_REG_SEC_ALARM               0x01
48 #define NVRAM_REG_MIN                     0x02
49 #define NVRAM_REG_MIN_ALARM               0x03
50 #define NVRAM_REG_HOUR                    0x04
51 #define NVRAM_REG_HOUR_ALARM              0x05
52 #define NVRAM_REG_WEEK_DAY                0x06
53 #define NVRAM_REG_MONTH_DAY               0x07
54 #define NVRAM_REG_MONTH                   0x08
55 #define NVRAM_REG_YEAR                    0x09
56 #define NVRAM_REG_STAT_A                  0x0a
57 #define NVRAM_REG_STAT_B                  0x0b
58 #define NVRAM_REG_STAT_C                  0x0c
59 #define NVRAM_REG_STAT_D                  0x0d
60 #define NVRAM_REG_DIAGNOSTIC_STATUS       0x0e  
61 #define NVRAM_REG_SHUTDOWN_STATUS         0x0f
62
63 #define NVRAM_IBM_HD_DATA                 0x12
64
65 #define NVRAM_REG_FLOPPY_TYPE             0x10
66 #define NVRAM_REG_EQUIPMENT_BYTE          0x14
67
68 #define NVRAM_REG_BASE_MEMORY_HIGH        0x16
69 #define NVRAM_REG_BASE_MEMORY_LOW         0x15
70
71 #define NVRAM_REG_EXT_MEMORY_HIGH         0x18
72 #define NVRAM_REG_EXT_MEMORY_LOW          0x17
73
74 #define NVRAM_REG_EXT_MEMORY_2ND_HIGH     0x31
75 #define NVRAM_REG_EXT_MEMORY_2ND_LOW      0x30
76
77 #define NVRAM_REG_BOOTSEQ_OLD             0x2d
78
79 #define NVRAM_REG_AMI_BIG_MEMORY_HIGH     0x35
80 #define NVRAM_REG_AMI_BIG_MEMORY_LOW      0x34
81
82
83 #define NVRAM_REG_CSUM_HIGH               0x2e
84 #define NVRAM_REG_CSUM_LOW                0x2f
85 #define NVRAM_REG_IBM_CENTURY_BYTE        0x32  
86 #define NVRAM_REG_IBM_PS2_CENTURY_BYTE    0x37  
87
88 #define NVRAM_REG_BOOTSEQ_NEW_FIRST       0x3D
89 #define NVRAM_REG_BOOTSEQ_NEW_SECOND      0x38
90
91
92 struct nvram_internal {
93   nvram_state_t dev_state;
94   uchar_t       thereg;
95   uchar_t       mem_state[NVRAM_REG_MAX];
96
97   uint_t        us;   //microseconds - for clock update - zeroed every second
98   uint_t        pus;  //microseconds - for periodic interrupt - cleared every period
99 };
100
101
102 struct rtc_stata {
103   uint_t        rate: 4;  // clock rate = 65536Hz / 2 rate (0110=1024 Hz)
104   uint_t        basis: 3; // time base, 010 = 32,768 Hz
105   uint_t        uip: 1;   // 1=update in progress
106 } __attribute__((__packed__)) __attribute__((__aligned__ (1)))  ;
107
108 struct rtc_statb {
109   uint_t        sum: 1;  // 1=summer (daylight savings)
110   uint_t        h24: 1;  // 1=24h clock
111   uint_t        dm: 1;   // 1=date/time is in bcd, 0=binary
112   uint_t        rec: 1;  // 1=rectangular signal
113   uint_t        ui: 1;   // 1=update interrupt
114   uint_t        ai: 1;   // 1=alarm interrupt
115   uint_t        pi: 1;   // 1=periodic interrupt
116   uint_t        set: 1;  // 1=blocked update
117 } __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
118
119 struct rtc_statc {
120   uint_t        res: 4;   // reserved
121   uint_t        uf: 1;    // 1=source of interrupt is update
122   uint_t        af: 1;    // 1=source of interrupt is alarm interrupt
123   uint_t        pf: 1;    // 1=source of interrupt is periodic interrupt
124   uint_t        irq: 1;   // 1=interrupt requested
125 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
126
127 struct rtc_statd {
128   uint_t        res: 7;   // reserved
129   uint_t        val: 1;   // 1=cmos ram data is OK
130 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
131
132
133
134
135 struct bcd_num {
136   uchar_t bot : 4;
137   uchar_t top : 4;
138 };
139
140
141
142 static uchar_t add_to(uchar_t * left, uchar_t * right, uchar_t bcd) {
143   uchar_t temp;
144
145   if (bcd) { 
146     struct bcd_num * bl = (struct bcd_num *)left;
147     struct bcd_num * br = (struct bcd_num *)right;
148     uchar_t carry = 0;
149
150     bl->bot += br->bot;
151     carry = bl->bot / 0xa;
152     bl->bot %= 0xa;
153
154     bl->top += carry + br->top;
155     carry = bl->top / 0xa;
156     bl->top %= 0xa;
157
158     return carry;
159   } else {
160     temp = *left;
161     *left += *right;
162
163     if (*left < temp) { 
164       return 1;
165     } else {
166       return 0;
167     }
168   }
169 }
170
171
172 static uchar_t days_in_month(struct vm_device * dev, uchar_t month, uchar_t bcd) {
173   // This completely ignores Julian / Gregorian stuff right now
174
175   if (bcd) { 
176
177     switch (month) 
178       {
179       case 0x1: //jan
180       case 0x3: //march
181       case 0x5: //may
182       case 0x7: //july
183       case 0x8: //aug
184       case 0x10: //oct
185       case 0x12: //dec
186         return 0x31;
187         break;
188       case 0x4: //april
189       case 0x6: //june
190       case 0x9: //sept
191       case 0x11: //nov
192         return 0x30;
193         break;
194       case 0x2: //feb
195         return 0x28;
196         break;
197       default:
198         return 0x30;
199       }
200     
201   } else {
202
203     switch (month) 
204       {
205       case 1: //jan
206       case 3: //march
207       case 5: //may
208       case 7: //july
209       case 8: //aug
210       case 10: //oct
211       case 12: //dec
212         return 31;
213         break;
214       case 4: //april
215       case 6: //june
216       case 9: //sept
217       case 11: //nov
218         return 30;
219         break;
220       case 2: //feb
221         return 28;
222         break;
223       default:
224         return 30;
225       }
226   }
227 }
228
229
230 static void update_time(struct vm_device * dev, uint_t period_us) {
231   struct nvram_internal * data = (struct nvram_internal *) (dev->private_data);
232   struct rtc_stata * stata = (struct rtc_stata *) &((data->mem_state[NVRAM_REG_STAT_A]));
233   struct rtc_statb * statb = (struct rtc_statb *) &((data->mem_state[NVRAM_REG_STAT_B]));
234   struct rtc_statc * statc = (struct rtc_statc *) &((data->mem_state[NVRAM_REG_STAT_C]));
235   //struct rtc_statd *statd = (struct rtc_statd *) &((data->mem_state[NVRAM_REG_STAT_D]));
236   uchar_t * sec = (uchar_t *) &(data->mem_state[NVRAM_REG_SEC]);
237   uchar_t * min = (uchar_t *) &(data->mem_state[NVRAM_REG_MIN]);
238   uchar_t * hour = (uchar_t *) &(data->mem_state[NVRAM_REG_HOUR]);
239   uchar_t * weekday = (uchar_t *) &(data->mem_state[NVRAM_REG_WEEK_DAY]);
240   uchar_t * monthday = (uchar_t *) &(data->mem_state[NVRAM_REG_MONTH_DAY]);
241   uchar_t * month = (uchar_t *) &(data->mem_state[NVRAM_REG_MONTH]);
242   uchar_t * year = (uchar_t *) &(data->mem_state[NVRAM_REG_YEAR]);
243   uchar_t * cent = (uchar_t *) &(data->mem_state[NVRAM_REG_IBM_CENTURY_BYTE]);
244   uchar_t * seca = (uchar_t *) &(data->mem_state[NVRAM_REG_SEC_ALARM]);
245   uchar_t * mina = (uchar_t *) &(data->mem_state[NVRAM_REG_MIN_ALARM]);
246   uchar_t * houra = (uchar_t *) &(data->mem_state[NVRAM_REG_HOUR_ALARM]);
247   uchar_t hour24;
248
249   uchar_t bcd = (statb->dm == 1);
250   uchar_t carry = 0;
251   uchar_t nextday = 0;
252   uint_t  periodic_period;
253
254   //PrintDebug("nvram: sizeof(struct rtc_stata)=%d\n", sizeof(struct rtc_stata));
255
256
257   //PrintDebug("nvram: update_time\n",statb->pi);
258   
259   // We will set these flags on exit
260   statc->irq = 0;
261   statc->pf = 0;
262   statc->af = 0;
263   statc->uf = 0;
264
265   // We will reset us after one second
266   data->us += period_us;
267   // We will reset pus after one periodic_period
268   data->pus += period_us;
269
270   if (data->us > 1000000) { 
271     carry = 1;
272     carry = add_to(sec, &carry, bcd);
273
274     if (carry) { 
275       PrintDebug("nvram: somehow managed to get a carry in second update\n"); 
276     }
277
278     if ( (bcd && (*sec == 0x60)) || 
279          ((!bcd) && (*sec == 60))) { 
280   
281       *sec = 0;
282
283       carry = 1;
284       carry = add_to(min, &carry, bcd);
285       if (carry) { 
286         PrintDebug("nvram: somehow managed to get a carry in minute update\n"); 
287       }
288
289       if ( (bcd && (*min == 0x60)) || 
290            ((!bcd) && (*min == 60))) { 
291
292         *min = 0;
293         hour24 = *hour;
294
295         if (!(statb->h24)) { 
296
297           if (hour24 & 0x80) { 
298             hour24 &= 0x8f;
299             uchar_t temp = ((bcd) ? 0x12 : 12);
300             add_to(&hour24, &temp, bcd);
301           }
302         }
303
304         carry = 1;
305         carry = add_to(&hour24, &carry, bcd);
306         if (carry) { 
307           PrintDebug("nvram: somehow managed to get a carry in hour update\n"); 
308         }
309
310         if ( (bcd && (hour24 == 0x24)) || 
311              ((!bcd) && (hour24 == 24))) { 
312           carry = 1;
313           nextday = 1;
314           hour24 = 0;
315         } else {
316           carry = 0;
317         }
318
319
320         if (statb->h24) { 
321           *hour = hour24;
322         } else {
323           if ( (bcd && (hour24 < 0x12)) || 
324                ((!bcd) && (hour24 < 12))) { 
325             *hour = hour24;
326
327           } else {
328
329             if (!bcd) { 
330               *hour = (hour24 - 12) | 0x80;
331             } else {
332               *hour = hour24;
333               struct bcd_num * n = (struct bcd_num *)hour;
334
335               if (n->bot < 0x2) { 
336                 n->top--;
337                 n->bot += 0xa;
338               }
339
340               n->bot -= 0x2;
341               n->top -= 0x1;
342             }
343           }
344         }
345
346         // now see if we need to carry into the days and further
347         if (nextday) { 
348           carry = 1;
349           add_to(weekday, &carry, bcd);
350
351           *weekday %= 0x7;  // same regardless of bcd
352
353           if ((*monthday) != days_in_month(dev, *month, bcd)) {
354             add_to(monthday, &carry, bcd);
355           } else {
356             *monthday = 0x1;
357
358             carry = 1;
359             add_to(month, &carry, bcd);
360
361             if ( (bcd && (*month == 0x13)) || 
362                  ((!bcd) && (*month == 13))) { 
363               *month = 1; // same for both 
364
365               carry = 1;
366               carry = add_to(year, &carry, bcd);
367
368               if ( (bcd && carry) || 
369                    ((!bcd) && (*year == 100))) { 
370                 *year = 0;
371                 carry = 1;
372                 add_to(cent, &carry, bcd);
373               }
374             }
375           }
376         }
377       }
378     }
379
380
381     data->us -= 1000000;
382     // OK, now check for the alarm, if it is set to interrupt
383     if (statb->ai) { 
384       if ((*sec == *seca) && (*min == *mina) && (*hour == *houra)) { 
385         statc->af = 1;
386         PrintDebug("nvram: interrupt on alarm\n");
387       }
388     }
389   }
390
391   if (statb->pi) { 
392     periodic_period = 1000000 / (65536 / (0x1 << stata->rate));
393     if (data->pus >= periodic_period) { 
394       statc->pf = 1;
395       data->pus -= periodic_period;
396       PrintDebug("nvram: interrupt on periodic\n");
397     }
398   }
399
400   if (statb->ui) { 
401     statc->uf = 1;
402     PrintDebug("nvram: interrupt on update\n");
403   }
404
405   statc->irq = (statc->pf || statc->af || statc->uf);
406   
407   //PrintDebug("nvram: time is now: YMDHMS: 0x%x:0x%x:0x%x:0x%x:0x%x,0x%x bcd=%d\n", *year, *month, *monthday, *hour, *min, *sec,bcd);
408   
409   // Interrupt associated VM, if needed
410   if (statc->irq) { 
411     PrintDebug("nvram: injecting interrupt\n");
412     dev->vm->vm_ops.raise_irq(dev->vm, NVRAM_RTC_IRQ);
413   }
414 }
415
416
417 int handle_timer_event(struct guest_info * info, 
418                         struct v3_timer_event * evt, 
419                         void * priv_data) {
420
421   struct vm_device * dev = (struct vm_device *)priv_data;
422
423   if (dev) {
424     update_time(dev, evt->period_us);
425   }
426   
427   return 0;
428 }
429
430 static int set_nvram_defaults(struct vm_device * dev) {
431   struct nvram_internal * nvram_state = (struct nvram_internal *)dev->private_data;
432
433   //
434   // 2 1.44 MB floppy drives
435   //
436 #if 1
437   nvram_state->mem_state[NVRAM_REG_FLOPPY_TYPE] = 0x44;
438 #else
439   nvram_state->mem_state[NVRAM_REG_FLOPPY_TYPE] = 0x00;
440 #endif
441
442   //
443   // For old boot sequence style, do floppy first
444   //
445   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_OLD] = 0x10;
446
447 #if 0
448   // For new boot sequence style, do floppy, cd, then hd
449   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_FIRST] = 0x31;
450   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_SECOND] = 0x20;
451 #endif
452
453   // For new boot sequence style, do cd, hd, floppy
454   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_FIRST] = 0x23;
455   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_SECOND] = 0x10;
456  
457
458   // Set equipment byte to note 2 floppies, vga display, keyboard,math,floppy
459   nvram_state->mem_state[NVRAM_REG_EQUIPMENT_BYTE] = 0x4f;
460   //nvram_state->mem_state[NVRAM_REG_EQUIPMENT_BYTE] = 0xf;
461
462   // Set conventional memory to 640K
463   nvram_state->mem_state[NVRAM_REG_BASE_MEMORY_HIGH] = 0x02;
464   nvram_state->mem_state[NVRAM_REG_BASE_MEMORY_LOW] = 0x80;
465
466   // Set extended memory to 15 MB
467   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_HIGH] = 0x3C;
468   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_LOW] = 0x00;
469   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_2ND_HIGH]= 0x3C;
470   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_2ND_LOW]= 0x00;
471
472   // Set the extended memory beyond 16 MB to 128-16 MB
473   nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_HIGH] = 0x7;
474   nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_LOW] = 0x00;
475
476   //nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_HIGH]= 0x00;
477   //nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_LOW]= 0x00;
478
479   
480   // This is the harddisk type.... Set accordingly...
481   nvram_state->mem_state[NVRAM_IBM_HD_DATA] = 0x20;
482
483   // Set the shutdown status gently
484   // soft reset
485   nvram_state->mem_state[NVRAM_REG_SHUTDOWN_STATUS] = 0x0;
486
487
488   // RTC status A
489   // 00100110 = no update in progress, base=32768 Hz, rate = 1024 Hz
490   nvram_state->mem_state[NVRAM_REG_STAT_A] = 0x26; 
491
492   // RTC status B
493   // 00000100 = not setting, no interrupts, blocked rect signal, bcd mode, 24 hour, normal time
494   nvram_state->mem_state[NVRAM_REG_STAT_B] = 0x06; 
495
496
497   // RTC status C
498   // No IRQ requested, result not do to any source
499   nvram_state->mem_state[NVRAM_REG_STAT_C] = 0x00;
500
501   // RTC status D
502   // Battery is OK
503   nvram_state->mem_state[NVRAM_REG_STAT_D] = 0x80;
504
505
506   // january 1, 2008, 00:00:00
507   nvram_state->mem_state[NVRAM_REG_MONTH] = 0x1;
508   nvram_state->mem_state[NVRAM_REG_MONTH_DAY] = 0x1;
509   nvram_state->mem_state[NVRAM_REG_WEEK_DAY] = 0x1;
510   nvram_state->mem_state[NVRAM_REG_YEAR] = 0x08;
511
512   nvram_state->us = 0;
513   nvram_state->pus = 0;
514
515   return 0;
516 }
517
518
519 int nvram_reset_device(struct vm_device * dev) {
520   struct nvram_internal * data = (struct nvram_internal *) dev->private_data;
521   
522   PrintDebug("nvram: reset device\n");
523  
524   data->dev_state = NVRAM_READY;
525   data->thereg = 0;
526   
527   return 0;
528 }
529
530
531
532
533
534 int nvram_start_device(struct vm_device * dev) {
535   PrintDebug("nvram: start device\n");
536   return 0;
537 }
538
539
540 int nvram_stop_device(struct vm_device * dev) {
541   PrintDebug("nvram: stop device\n");
542   return 0;
543 }
544
545
546
547
548 int nvram_write_reg_port(ushort_t port,
549                          void * src, 
550                          uint_t length,
551                          struct vm_device * dev) {
552   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
553
554   memcpy(&(data->thereg), src, 1);
555   PrintDebug("Writing To NVRAM reg: 0x%x\n", data->thereg);
556
557
558   return 1;
559 }
560
561 int nvram_read_data_port(ushort_t port,
562                          void * dst, 
563                          uint_t length,
564                          struct vm_device * dev) {
565   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
566
567   memcpy(dst, &(data->mem_state[data->thereg]), 1);
568
569   PrintDebug("nvram_read_data_port(0x%x)=0x%x\n", data->thereg, data->mem_state[data->thereg]);
570
571   // hack
572   if (data->thereg == NVRAM_REG_STAT_A) { 
573     data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
574   }
575
576
577   return 1;
578 }
579
580 int nvram_write_data_port(ushort_t port,
581                           void * src, 
582                           uint_t length,
583                           struct vm_device * dev) {
584   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
585
586   memcpy(&(data->mem_state[data->thereg]), src, 1);
587
588   PrintDebug("nvram_write_data_port(0x%x)=0x%x\n", data->thereg, data->mem_state[data->thereg]);
589
590   return 1;
591 }
592
593
594
595 int nvram_init_device(struct vm_device * dev) {
596  
597   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
598
599   PrintDebug("nvram: init_device\n");
600
601   memset(data->mem_state, 0, NVRAM_REG_MAX);
602
603   // Would read state here
604   set_nvram_defaults(dev);
605
606   nvram_reset_device(dev);
607
608   // hook ports
609   dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
610   dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
611   
612   v3_hook_host_event(dev->vm, HOST_TIMER_EVT, V3_HOST_EVENT_HANDLER(handle_timer_event), dev);
613
614   return 0;
615 }
616
617 int nvram_deinit_device(struct vm_device * dev) {
618   dev_unhook_io(dev, NVRAM_REG_PORT);
619   dev_unhook_io(dev, NVRAM_DATA_PORT);
620
621   nvram_reset_device(dev);
622   return 0;
623 }
624
625
626
627
628
629 static struct vm_device_ops dev_ops = { 
630   .init = nvram_init_device, 
631   .deinit = nvram_deinit_device,
632   .reset = nvram_reset_device,
633   .start = nvram_start_device,
634   .stop = nvram_stop_device,
635 };
636
637
638
639
640 struct vm_device * create_nvram() {
641   struct nvram_internal * nvram_state = NULL;
642
643   nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
644
645   PrintDebug("nvram: internal at %x\n", nvram_state);
646
647   struct vm_device * device = create_device("NVRAM", &dev_ops, nvram_state);
648
649   return device;
650 }