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