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.


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