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 new copyright and license
[palacios-OLD.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 vm_device * thedev = NULL;
136
137 static struct vm_device * demultiplex_timer_interrupt(uint_t period_us)
138 {
139   // hack
140   return thedev;
141 }
142
143 struct bcd_num {
144   uchar_t bot : 4;
145   uchar_t top : 4;
146 } ;
147
148 static uchar_t add_to(uchar_t * left, uchar_t * right, uchar_t bcd)
149 {
150   uchar_t temp;
151
152   if (bcd) { 
153     struct bcd_num * bl = (struct bcd_num *)left;
154     struct bcd_num * br = (struct bcd_num *)right;
155     uchar_t carry = 0;
156
157     bl->bot += br->bot;
158     carry = bl->bot / 0xa;
159     bl->bot %= 0xa;
160
161     bl->top += carry + br->top;
162     carry = bl->top / 0xa;
163     bl->top %= 0xa;
164
165     return carry;
166   } else {
167     temp = *left;
168     *left += *right;
169
170     if (*left < temp) { 
171       return 1;
172     } else {
173       return 0;
174     }
175   }
176   
177 }
178
179
180 static uchar_t days_in_month(struct vm_device *dev, uchar_t month, uchar_t bcd)
181 {
182   // This completely ignores Julian / Gregorian stuff right now
183
184   if (bcd) { 
185
186     switch (month) 
187       {
188       case 0x1: //jan
189       case 0x3: //march
190       case 0x5: //may
191       case 0x7: //july
192       case 0x8: //aug
193       case 0x10: //oct
194       case 0x12: //dec
195         return 0x31;
196         break;
197       case 0x4: //april
198       case 0x6: //june
199       case 0x9: //sept
200       case 0x11: //nov
201         return 0x30;
202         break;
203       case 0x2: //feb
204         return 0x28;
205         break;
206       default:
207         return 0x30;
208       }
209     
210   } else {
211
212     switch (month) 
213       {
214       case 1: //jan
215       case 3: //march
216       case 5: //may
217       case 7: //july
218       case 8: //aug
219       case 10: //oct
220       case 12: //dec
221         return 31;
222         break;
223       case 4: //april
224       case 6: //june
225       case 9: //sept
226       case 11: //nov
227         return 30;
228         break;
229       case 2: //feb
230         return 28;
231         break;
232       default:
233         return 30;
234       }
235   }
236 }
237
238
239 static void update_time(struct vm_device *dev, uint_t period_us)
240 {
241   struct nvram_internal * data = (struct nvram_internal *) (dev->private_data);
242   struct rtc_stata * stata = (struct rtc_stata *) &((data->mem_state[NVRAM_REG_STAT_A]));
243   struct rtc_statb * statb = (struct rtc_statb *) &((data->mem_state[NVRAM_REG_STAT_B]));
244   struct rtc_statc * statc = (struct rtc_statc *) &((data->mem_state[NVRAM_REG_STAT_C]));
245   //struct rtc_statd *statd = (struct rtc_statd *) &((data->mem_state[NVRAM_REG_STAT_D]));
246   uchar_t * sec = (uchar_t *) &(data->mem_state[NVRAM_REG_SEC]);
247   uchar_t * min = (uchar_t *) &(data->mem_state[NVRAM_REG_MIN]);
248   uchar_t * hour = (uchar_t *) &(data->mem_state[NVRAM_REG_HOUR]);
249   uchar_t * weekday = (uchar_t *) &(data->mem_state[NVRAM_REG_WEEK_DAY]);
250   uchar_t * monthday = (uchar_t *) &(data->mem_state[NVRAM_REG_MONTH_DAY]);
251   uchar_t * month = (uchar_t *) &(data->mem_state[NVRAM_REG_MONTH]);
252   uchar_t * year = (uchar_t *) &(data->mem_state[NVRAM_REG_YEAR]);
253   uchar_t * cent = (uchar_t *) &(data->mem_state[NVRAM_REG_IBM_CENTURY_BYTE]);
254   uchar_t * seca = (uchar_t *) &(data->mem_state[NVRAM_REG_SEC_ALARM]);
255   uchar_t * mina = (uchar_t *) &(data->mem_state[NVRAM_REG_MIN_ALARM]);
256   uchar_t * houra = (uchar_t *) &(data->mem_state[NVRAM_REG_HOUR_ALARM]);
257   uchar_t hour24;
258
259   uchar_t bcd = (statb->dm == 1);
260   uchar_t carry = 0;
261   uchar_t nextday = 0;
262   uint_t  periodic_period;
263
264   //PrintDebug("nvram: sizeof(struct rtc_stata)=%d\n", sizeof(struct rtc_stata));
265
266
267   //PrintDebug("nvram: update_time\n",statb->pi);
268   
269   // We will set these flags on exit
270   statc->irq = 0;
271   statc->pf = 0;
272   statc->af = 0;
273   statc->uf = 0;
274
275   // We will reset us after one second
276   data->us += period_us;
277   // We will reset pus after one periodic_period
278   data->pus += period_us;
279
280   if (data->us > 1000000) { 
281     carry = 1;
282     carry = add_to(sec, &carry, bcd);
283
284     if (carry) { 
285       PrintDebug("nvram: somehow managed to get a carry in second update\n"); 
286     }
287
288     if ( (bcd && (*sec == 0x60)) || 
289          ((!bcd) && (*sec == 60))) { 
290   
291       *sec = 0;
292
293       carry = 1;
294       carry = add_to(min, &carry, bcd);
295       if (carry) { 
296         PrintDebug("nvram: somehow managed to get a carry in minute update\n"); 
297       }
298
299       if ( (bcd && (*min == 0x60)) || 
300            ((!bcd) && (*min == 60))) { 
301
302         *min = 0;
303         hour24 = *hour;
304
305         if (!(statb->h24)) { 
306
307           if (hour24 & 0x80) { 
308             hour24 &= 0x8f;
309             uchar_t temp = ((bcd) ? 0x12 : 12);
310             add_to(&hour24, &temp, bcd);
311           }
312         }
313
314         carry = 1;
315         carry = add_to(&hour24, &carry, bcd);
316         if (carry) { 
317           PrintDebug("nvram: somehow managed to get a carry in hour update\n"); 
318         }
319
320         if ( (bcd && (hour24 == 0x24)) || 
321              ((!bcd) && (hour24 == 24))) { 
322           carry = 1;
323           nextday = 1;
324           hour24 = 0;
325         } else {
326           carry = 0;
327         }
328
329
330         if (statb->h24) { 
331           *hour = hour24;
332         } else {
333           if ( (bcd && (hour24 < 0x12)) || 
334                ((!bcd) && (hour24 < 12))) { 
335             *hour = hour24;
336
337           } else {
338
339             if (!bcd) { 
340               *hour = (hour24 - 12) | 0x80;
341             } else {
342               *hour = hour24;
343               struct bcd_num * n = (struct bcd_num *)hour;
344
345               if (n->bot < 0x2) { 
346                 n->top--;
347                 n->bot += 0xa;
348               }
349
350               n->bot -= 0x2;
351               n->top -= 0x1;
352             }
353           }
354         }
355
356         // now see if we need to carry into the days and further
357         if (nextday) { 
358           carry = 1;
359           add_to(weekday, &carry, bcd);
360
361           *weekday %= 0x7;  // same regardless of bcd
362
363           if ((*monthday) != days_in_month(dev, *month, bcd)) {
364             add_to(monthday, &carry, bcd);
365           } else {
366             *monthday = 0x1;
367
368             carry = 1;
369             add_to(month, &carry, bcd);
370
371             if ( (bcd && (*month == 0x13)) || 
372                  ((!bcd) && (*month == 13))) { 
373               *month = 1; // same for both 
374
375               carry = 1;
376               carry = add_to(year, &carry, bcd);
377
378               if ( (bcd && carry) || 
379                    ((!bcd) && (*year == 100))) { 
380                 *year = 0;
381                 carry = 1;
382                 add_to(cent, &carry, bcd);
383               }
384             }
385           }
386         }
387       }
388     }
389
390
391     data->us -= 1000000;
392     // OK, now check for the alarm, if it is set to interrupt
393     if (statb->ai) { 
394       if ((*sec == *seca) && (*min == *mina) && (*hour == *houra)) { 
395         statc->af = 1;
396         PrintDebug("nvram: interrupt on alarm\n");
397       }
398     }
399   }
400
401   if (statb->pi) { 
402     periodic_period = 1000000 / (65536 / (0x1 << stata->rate));
403     if (data->pus >= periodic_period) { 
404       statc->pf = 1;
405       data->pus -= periodic_period;
406       PrintDebug("nvram: interrupt on periodic\n");
407     }
408   }
409
410   if (statb->ui) { 
411     statc->uf = 1;
412     PrintDebug("nvram: interrupt on update\n");
413   }
414
415   statc->irq = (statc->pf || statc->af || statc->uf);
416   
417   //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);
418   
419   // Interrupt associated VM, if needed
420   if (statc->irq) { 
421     PrintDebug("nvram: injecting interrupt\n");
422     dev->vm->vm_ops.raise_irq(dev->vm, NVRAM_RTC_IRQ);
423   }
424 }
425
426
427
428
429 void deliver_timer_interrupt_to_vmm(uint_t period_us)
430 {
431   struct vm_device * dev = demultiplex_timer_interrupt(period_us);
432
433   if (dev) {
434     update_time(dev, period_us);
435   }
436   
437 }
438
439
440 static int set_nvram_defaults(struct vm_device * dev)
441 {
442   struct nvram_internal * nvram_state = (struct nvram_internal *)dev->private_data;
443
444
445   //
446   // 2 1.44 MB floppy drives
447   //
448 #if 1
449   nvram_state->mem_state[NVRAM_REG_FLOPPY_TYPE] = 0x44;
450 #else
451   nvram_state->mem_state[NVRAM_REG_FLOPPY_TYPE] = 0x00;
452 #endif
453
454   //
455   // For old boot sequence style, do floppy first
456   //
457   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_OLD] = 0x10;
458
459 #if 0
460   // For new boot sequence style, do floppy, cd, then hd
461   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_FIRST] = 0x31;
462   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_SECOND] = 0x20;
463 #endif
464
465   // For new boot sequence style, do cd, hd, floppy
466   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_FIRST] = 0x23;
467   nvram_state->mem_state[NVRAM_REG_BOOTSEQ_NEW_SECOND] = 0x10;
468  
469
470   // Set equipment byte to note 2 floppies, vga display, keyboard,math,floppy
471   nvram_state->mem_state[NVRAM_REG_EQUIPMENT_BYTE] = 0x4f;
472   //nvram_state->mem_state[NVRAM_REG_EQUIPMENT_BYTE] = 0xf;
473
474   // Set conventional memory to 640K
475   nvram_state->mem_state[NVRAM_REG_BASE_MEMORY_HIGH] = 0x02;
476   nvram_state->mem_state[NVRAM_REG_BASE_MEMORY_LOW] = 0x80;
477
478   // Set extended memory to 15 MB
479   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_HIGH] = 0x3C;
480   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_LOW] = 0x00;
481   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_2ND_HIGH]= 0x3C;
482   nvram_state->mem_state[NVRAM_REG_EXT_MEMORY_2ND_LOW]= 0x00;
483
484   // Set the extended memory beyond 16 MB to 128-16 MB
485   nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_HIGH] = 0x7;
486   nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_LOW] = 0x00;
487
488   //nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_HIGH]= 0x00;
489   //nvram_state->mem_state[NVRAM_REG_AMI_BIG_MEMORY_LOW]= 0x00;
490
491   
492   // This is the harddisk type.... Set accordingly...
493   nvram_state->mem_state[NVRAM_IBM_HD_DATA] = 0x20;
494
495   // Set the shutdown status gently
496   // soft reset
497   nvram_state->mem_state[NVRAM_REG_SHUTDOWN_STATUS] = 0x0;
498
499
500   // RTC status A
501   // 00100110 = no update in progress, base=32768 Hz, rate = 1024 Hz
502   nvram_state->mem_state[NVRAM_REG_STAT_A] = 0x26; 
503
504   // RTC status B
505   // 00000100 = not setting, no interrupts, blocked rect signal, bcd mode, 24 hour, normal time
506   nvram_state->mem_state[NVRAM_REG_STAT_B] = 0x06; 
507
508
509   // RTC status C
510   // No IRQ requested, result not do to any source
511   nvram_state->mem_state[NVRAM_REG_STAT_C] = 0x00;
512
513   // RTC status D
514   // Battery is OK
515   nvram_state->mem_state[NVRAM_REG_STAT_D] = 0x80;
516
517
518   // january 1, 2008, 00:00:00
519   nvram_state->mem_state[NVRAM_REG_MONTH] = 0x1;
520   nvram_state->mem_state[NVRAM_REG_MONTH_DAY] = 0x1;
521   nvram_state->mem_state[NVRAM_REG_WEEK_DAY] = 0x1;
522   nvram_state->mem_state[NVRAM_REG_YEAR] = 0x08;
523
524   nvram_state->us = 0;
525   nvram_state->pus = 0;
526
527   return 0;
528
529 }
530
531
532 int nvram_reset_device(struct vm_device * dev)
533 {
534   struct nvram_internal * data = (struct nvram_internal *) dev->private_data;
535   
536   PrintDebug("nvram: reset device\n");
537
538  
539   data->dev_state = NVRAM_READY;
540   data->thereg = 0;
541   
542   return 0;
543 }
544
545
546
547
548
549 int nvram_start_device(struct vm_device *dev)
550 {
551   PrintDebug("nvram: start device\n");
552   return 0;
553 }
554
555
556 int nvram_stop_device(struct vm_device *dev)
557 {
558   PrintDebug("nvram: stop device\n");
559   return 0;
560 }
561
562
563
564
565 int nvram_write_reg_port(ushort_t port,
566                          void * src, 
567                          uint_t length,
568                          struct vm_device * dev)
569 {
570   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
571
572   memcpy(&(data->thereg), src, 1);
573   PrintDebug("Writing To NVRAM reg: 0x%x\n", data->thereg);
574
575
576   return 1;
577 }
578
579 int nvram_read_data_port(ushort_t port,
580                          void * dst, 
581                          uint_t length,
582                          struct vm_device * dev)
583 {
584   struct nvram_internal * data = (struct nvram_internal *) dev->private_data;
585
586
587
588   memcpy(dst, &(data->mem_state[data->thereg]), 1);
589
590   PrintDebug("nvram_read_data_port(0x%x)=0x%x\n", data->thereg, data->mem_state[data->thereg]);
591
592   // hack
593   if (data->thereg == NVRAM_REG_STAT_A) { 
594     data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
595   }
596
597
598   return 1;
599 }
600
601 int nvram_write_data_port(ushort_t port,
602                           void * src, 
603                           uint_t length,
604                           struct vm_device * dev)
605 {
606   struct nvram_internal * data = (struct nvram_internal *)dev->private_data;
607
608   memcpy(&(data->mem_state[data->thereg]), src, 1);
609
610   PrintDebug("nvram_write_data_port(0x%x)=0x%x\n", data->thereg, data->mem_state[data->thereg]);
611
612   return 1;
613 }
614
615
616
617 int nvram_init_device(struct vm_device * dev) {
618  
619   struct nvram_internal * data = (struct nvram_internal *) dev->private_data;
620
621   PrintDebug("nvram: init_device\n");
622
623   memset(data->mem_state, 0, NVRAM_REG_MAX);
624
625   // Would read state here
626   set_nvram_defaults(dev);
627
628   nvram_reset_device(dev);
629
630   // hook ports
631   dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
632   dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
633   
634   return 0;
635 }
636
637 int nvram_deinit_device(struct vm_device *dev)
638 {
639
640
641   dev_unhook_io(dev, NVRAM_REG_PORT);
642   dev_unhook_io(dev, NVRAM_DATA_PORT);
643
644   nvram_reset_device(dev);
645   return 0;
646 }
647
648
649
650
651
652 static struct vm_device_ops dev_ops = { 
653   .init = nvram_init_device, 
654   .deinit = nvram_deinit_device,
655   .reset = nvram_reset_device,
656   .start = nvram_start_device,
657   .stop = nvram_stop_device,
658 };
659
660
661
662
663 struct vm_device * create_nvram() {
664   struct nvram_internal * nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
665
666   PrintDebug("nvram: internal at %x\n",nvram_state);
667
668   struct vm_device * device = create_device("NVRAM", &dev_ops, nvram_state);
669
670   if (thedev != NULL) {
671     PrintDebug("nvram: warning! overwriting thedev\n");
672   }
673
674   thedev = device;
675
676   return device;
677 }