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