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