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.


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