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