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.


Converted NVRAM RTC to use timers instead of the host timer event
[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 <palacios/vmm_dev_mgr.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_types.h>
24
25 #include <palacios/vmm_lock.h>
26
27 #include <devices/ide.h>
28 #include <palacios/vmm_intr.h>
29 #include <palacios/vmm_host_events.h>
30 #include <palacios/vm_guest.h>
31
32
33 #ifndef CONFIG_DEBUG_NVRAM
34 #undef PrintDebug
35 #define PrintDebug(fmt, args...)
36 #endif
37
38
39 #define NVRAM_REG_PORT  0x70
40 #define NVRAM_DATA_PORT 0x71
41
42 #define NVRAM_RTC_IRQ   0x8
43
44
45 typedef enum {NVRAM_READY, NVRAM_REG_POSTED} nvram_state_t;
46
47
48 #define NVRAM_REG_MAX   256
49
50
51 // These are borrowed from Bochs, which borrowed from
52 // Ralf Brown's interupt list, and extended
53 #define NVRAM_REG_SEC                     0x00
54 #define NVRAM_REG_SEC_ALARM               0x01
55 #define NVRAM_REG_MIN                     0x02
56 #define NVRAM_REG_MIN_ALARM               0x03
57 #define NVRAM_REG_HOUR                    0x04
58 #define NVRAM_REG_HOUR_ALARM              0x05
59 #define NVRAM_REG_WEEK_DAY                0x06
60 #define NVRAM_REG_MONTH_DAY               0x07
61 #define NVRAM_REG_MONTH                   0x08
62 #define NVRAM_REG_YEAR                    0x09
63 #define NVRAM_REG_STAT_A                  0x0a
64 #define NVRAM_REG_STAT_B                  0x0b
65 #define NVRAM_REG_STAT_C                  0x0c
66 #define NVRAM_REG_STAT_D                  0x0d
67 #define NVRAM_REG_DIAGNOSTIC_STATUS       0x0e  
68 #define NVRAM_REG_SHUTDOWN_STATUS         0x0f
69
70 #define NVRAM_IBM_HD_DATA                 0x12
71 #define NVRAM_IDE_TRANSLATION             0x39
72
73 #define NVRAM_REG_FLOPPY_TYPE             0x10
74 #define NVRAM_REG_EQUIPMENT_BYTE          0x14
75
76 #define NVRAM_REG_BASE_MEMORY_HIGH        0x16
77 #define NVRAM_REG_BASE_MEMORY_LOW         0x15
78
79 #define NVRAM_REG_EXT_MEMORY_HIGH         0x18
80 #define NVRAM_REG_EXT_MEMORY_LOW          0x17
81
82 #define NVRAM_REG_EXT_MEMORY_2ND_HIGH     0x31
83 #define NVRAM_REG_EXT_MEMORY_2ND_LOW      0x30
84
85 #define NVRAM_REG_BOOTSEQ_OLD             0x2d
86
87 #define NVRAM_REG_AMI_BIG_MEMORY_HIGH     0x35
88 #define NVRAM_REG_AMI_BIG_MEMORY_LOW      0x34
89
90 #define NVRAM_REG_CSUM_HIGH               0x2e
91 #define NVRAM_REG_CSUM_LOW                0x2f
92 #define NVRAM_REG_IBM_CENTURY_BYTE        0x32  
93 #define NVRAM_REG_IBM_PS2_CENTURY_BYTE    0x37  
94
95 #define NVRAM_REG_BOOTSEQ_NEW_FIRST       0x3D
96 #define NVRAM_REG_BOOTSEQ_NEW_SECOND      0x38
97
98 #define CHECKSUM_REGION_FIRST_BYTE        0x10
99 #define CHECKSUM_REGION_LAST_BYTE         0x2d
100
101
102 struct nvram_internal {
103     nvram_state_t dev_state;
104     uint8_t       thereg;
105     uint8_t       mem_state[NVRAM_REG_MAX];
106     uint8_t       reg_map[NVRAM_REG_MAX / 8];
107
108     struct vm_device * ide;
109
110     struct v3_vm_info * vm;
111     
112     struct v3_timer   *timer;
113
114     v3_lock_t nvram_lock;
115
116     uint32_t        us;   //microseconds - for clock update - zeroed every second
117     uint32_t        pus;  //microseconds - for periodic interrupt - cleared every period
118 };
119
120
121 struct rtc_stata {
122     uint8_t        rate   : 4;  // clock rate = 65536Hz / 2 rate (0110=1024 Hz)
123     uint8_t        basis  : 3;  // time base, 010 = 32,768 Hz
124     uint8_t        uip    : 1;  // 1=update in progress
125 } __attribute__((__packed__)) __attribute__((__aligned__ (1)))  ;
126
127 struct rtc_statb {
128     uint8_t        sum    : 1;  // 1=summer (daylight savings)
129     uint8_t        h24    : 1;  // 1=24h clock
130     uint8_t        dm     : 1;  // 1=date/time is in bcd, 0=binary
131     uint8_t        rec    : 1;  // 1=rectangular signal
132     uint8_t        ui     : 1;  // 1=update interrupt
133     uint8_t        ai     : 1;  // 1=alarm interrupt
134     uint8_t        pi     : 1;  // 1=periodic interrupt
135     uint8_t        set    : 1;  // 1=blocked update
136 } __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
137
138 struct rtc_statc {
139     uint8_t        res    : 4;  // reserved
140     uint8_t        uf     : 1;  // 1=source of interrupt is update
141     uint8_t        af     : 1;  // 1=source of interrupt is alarm interrupt
142     uint8_t        pf     : 1;  // 1=source of interrupt is periodic interrupt
143     uint8_t        irq    : 1;  // 1=interrupt requested
144 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
145
146 struct rtc_statd {
147     uint8_t        res    : 7;  // reserved
148     uint8_t        val    : 1;  // 1=cmos ram data is OK
149 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
150
151
152
153
154 struct bcd_num {
155     uint8_t bot : 4;
156     uint8_t top : 4;
157 } __attribute__((packed));;
158
159
160
161 static void set_reg_num(struct nvram_internal * nvram, uint8_t reg_num) {
162     int major = (reg_num / 8);
163     int minor = reg_num % 8;
164
165     nvram->reg_map[major] |= (0x1 << minor);
166 }
167
168 static int is_reg_set(struct nvram_internal * nvram, uint8_t reg_num) {
169     int major = (reg_num / 8);
170     int minor = reg_num % 8;
171     
172     return (nvram->reg_map[major] & (0x1 << minor)) ? 1 : 0;
173 }
174
175
176 static void set_memory(struct nvram_internal * nvram, uint8_t reg, uint8_t val) {
177     set_reg_num(nvram, reg);
178     nvram->mem_state[reg] = val;
179 }
180
181 static int get_memory(struct nvram_internal * nvram, uint8_t reg, uint8_t * val) {
182
183     if (!is_reg_set(nvram, reg)) {
184         *val = 0;
185         return -1;
186     }
187
188     *val = nvram->mem_state[reg];
189     return 0;
190 }
191
192
193 static uint8_t add_to(uint8_t * left, uint8_t * right, uint8_t bcd) {
194     uint8_t temp;
195
196     if (bcd) { 
197         struct bcd_num * bl = (struct bcd_num *)left;
198         struct bcd_num * br = (struct bcd_num *)right;
199         uint8_t carry = 0;
200
201         bl->bot += br->bot;
202         carry = bl->bot / 0xa;
203         bl->bot %= 0xa;
204
205         bl->top += carry + br->top;
206         carry = bl->top / 0xa;
207         bl->top %= 0xa;
208
209         return carry;
210     } else {
211         temp = *left;
212         *left += *right;
213
214         if (*left < temp) { 
215             return 1;
216         } else {
217             return 0;
218         }
219     }
220 }
221
222
223 static uint8_t days_in_month(uint8_t month, uint8_t bcd) {
224     // This completely ignores Julian / Gregorian stuff right now
225
226     if (bcd) { 
227
228         switch (month) 
229             {
230                 case 0x1: //jan
231                 case 0x3: //march
232                 case 0x5: //may
233                 case 0x7: //july
234                 case 0x8: //aug
235                 case 0x10: //oct
236                 case 0x12: //dec
237                     return 0x31;
238                     break;
239                 case 0x4: //april
240                 case 0x6: //june
241                 case 0x9: //sept
242                 case 0x11: //nov
243                     return 0x30;
244                     break;
245                 case 0x2: //feb
246                     return 0x28;
247                     break;
248                 default:
249                     return 0x30;
250             }
251     
252     } else {
253
254         switch (month) 
255             {
256                 case 1: //jan
257                 case 3: //march
258                 case 5: //may
259                 case 7: //july
260                 case 8: //aug
261                 case 10: //oct
262                 case 12: //dec
263                     return 31;
264                     break;
265                 case 4: //april
266                 case 6: //june
267                 case 9: //sept
268                 case 11: //nov
269                     return 30;
270                     break;
271                 case 2: //feb
272                     return 28;
273                     break;
274                 default:
275                     return 30;
276             }
277     }
278 }
279
280
281 static void update_time(struct nvram_internal * data, uint32_t period_us) {
282     struct rtc_stata * stata = (struct rtc_stata *)&((data->mem_state[NVRAM_REG_STAT_A]));
283     struct rtc_statb * statb = (struct rtc_statb *)&((data->mem_state[NVRAM_REG_STAT_B]));
284     struct rtc_statc * statc = (struct rtc_statc *)&((data->mem_state[NVRAM_REG_STAT_C]));
285     //struct rtc_statd *statd = (struct rtc_statd *) &((data->mem_state[NVRAM_REG_STAT_D]));
286     uint8_t * sec      = (uint8_t *)&(data->mem_state[NVRAM_REG_SEC]);
287     uint8_t * min      = (uint8_t *)&(data->mem_state[NVRAM_REG_MIN]);
288     uint8_t * hour     = (uint8_t *)&(data->mem_state[NVRAM_REG_HOUR]);
289     uint8_t * weekday  = (uint8_t *)&(data->mem_state[NVRAM_REG_WEEK_DAY]);
290     uint8_t * monthday = (uint8_t *)&(data->mem_state[NVRAM_REG_MONTH_DAY]);
291     uint8_t * month    = (uint8_t *)&(data->mem_state[NVRAM_REG_MONTH]);
292     uint8_t * year     = (uint8_t *)&(data->mem_state[NVRAM_REG_YEAR]);
293     uint8_t * cent     = (uint8_t *)&(data->mem_state[NVRAM_REG_IBM_CENTURY_BYTE]);
294     uint8_t * cent_ps2 = (uint8_t *)&(data->mem_state[NVRAM_REG_IBM_PS2_CENTURY_BYTE]);
295     uint8_t * seca     = (uint8_t *)&(data->mem_state[NVRAM_REG_SEC_ALARM]);
296     uint8_t * mina     = (uint8_t *)&(data->mem_state[NVRAM_REG_MIN_ALARM]);
297     uint8_t * houra    = (uint8_t *)&(data->mem_state[NVRAM_REG_HOUR_ALARM]);
298     uint8_t hour24;
299
300     uint8_t bcd = (statb->dm == 1);
301     uint8_t carry = 0;
302     uint8_t nextday = 0;
303     uint32_t  periodic_period;
304
305     //PrintDebug("nvram: sizeof(struct rtc_stata)=%d\n", sizeof(struct rtc_stata));
306
307
308     //PrintDebug("nvram: update_time\n",statb->pi);
309   
310     // We will set these flags on exit
311     statc->irq = 0;
312     statc->pf = 0;
313     statc->af = 0;
314     statc->uf = 0;
315
316     // We will reset us after one second
317     data->us += period_us;
318     // We will reset pus after one periodic_period
319     data->pus += period_us;
320
321     if (data->us > 1000000) { 
322         carry = 1;
323         carry = add_to(sec, &carry, bcd);
324
325         if (carry) { 
326             PrintDebug("nvram: somehow managed to get a carry in second update\n"); 
327         }
328
329         if ( (bcd && (*sec == 0x60)) || 
330              ((!bcd) && (*sec == 60))) { 
331   
332             *sec = 0;
333
334             carry = 1;
335             carry = add_to(min, &carry, bcd);
336             if (carry) { 
337                 PrintDebug("nvram: somehow managed to get a carry in minute update\n"); 
338             }
339
340             if ( (bcd && (*min == 0x60)) || 
341                  ((!bcd) && (*min == 60))) { 
342
343                 *min = 0;
344                 hour24 = *hour;
345
346                 if (!(statb->h24)) { 
347
348                     if (hour24 & 0x80) { 
349                         hour24 &= 0x8f;
350                         uint8_t temp = ((bcd) ? 0x12 : 12);
351                         add_to(&hour24, &temp, bcd);
352                     }
353                 }
354
355                 carry = 1;
356                 carry = add_to(&hour24, &carry, bcd);
357                 if (carry) { 
358                     PrintDebug("nvram: somehow managed to get a carry in hour update\n"); 
359                 }
360
361                 if ( (bcd && (hour24 == 0x24)) || 
362                      ((!bcd) && (hour24 == 24))) { 
363                     carry = 1;
364                     nextday = 1;
365                     hour24 = 0;
366                 } else {
367                     carry = 0;
368                 }
369
370
371                 if (statb->h24) { 
372                     *hour = hour24;
373                 } else {
374                     if ( (bcd && (hour24 < 0x12)) || 
375                          ((!bcd) && (hour24 < 12))) { 
376                         *hour = hour24;
377
378                     } else {
379
380                         if (!bcd) { 
381                             *hour = (hour24 - 12) | 0x80;
382                         } else {
383                             *hour = hour24;
384                             struct bcd_num * n = (struct bcd_num *)hour;
385
386                             if (n->bot < 0x2) { 
387                                 n->top--;
388                                 n->bot += 0xa;
389                             }
390
391                             n->bot -= 0x2;
392                             n->top -= 0x1;
393                         }
394                     }
395                 }
396
397                 // now see if we need to carry into the days and further
398                 if (nextday) { 
399                     carry = 1;
400                     add_to(weekday, &carry, bcd);
401
402                     *weekday %= 0x7;  // same regardless of bcd
403
404                     if ((*monthday) != days_in_month(*month, bcd)) {
405                         add_to(monthday, &carry, bcd);
406                     } else {
407                         *monthday = 0x1;
408
409                         carry = 1;
410                         add_to(month, &carry, bcd);
411
412                         if ( (bcd && (*month == 0x13)) || 
413                              ((!bcd) && (*month == 13))) { 
414                             *month = 1; // same for both 
415
416                             carry = 1;
417                             carry = add_to(year, &carry, bcd);
418
419                             if ( (bcd && carry) || 
420                                  ((!bcd) && (*year == 100))) { 
421                                 *year = 0;
422                                 carry = 1;
423                                 add_to(cent, &carry, bcd);
424                                 *cent_ps2 = *cent;
425                             }
426                         }
427                     }
428                 }
429             }
430         }
431
432
433         data->us -= 1000000;
434         // OK, now check for the alarm, if it is set to interrupt
435         if (statb->ai) { 
436             if ((*sec == *seca) && (*min == *mina) && (*hour == *houra)) { 
437                 statc->af = 1;
438                 PrintDebug("nvram: interrupt on alarm\n");
439             }
440         }
441     }
442
443     if (statb->pi) { 
444         periodic_period = 1000000 / (65536 / (0x1 << stata->rate));
445         if (data->pus >= periodic_period) { 
446             statc->pf = 1;
447             data->pus -= periodic_period;
448             PrintDebug("nvram: interrupt on periodic\n");
449         }
450     }
451
452     if (statb->ui) { 
453         statc->uf = 1;
454         PrintDebug("nvram: interrupt on update\n");
455     }
456
457     statc->irq = (statc->pf || statc->af || statc->uf);
458   
459     //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);
460   
461     // Interrupt associated VM, if needed
462     if (statc->irq) { 
463         PrintDebug("nvram: injecting interrupt\n");
464         v3_raise_irq(data->vm, NVRAM_RTC_IRQ);
465     }
466 }
467
468 /*
469 static int handle_timer_event(struct v3_vm_info * vm, 
470                               struct v3_timer_event * evt, 
471                               void * priv_data) {
472
473
474     struct nvram_internal * data = priv_data;
475
476     if (data) {
477         addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
478         update_time(data, evt->period_us);
479         v3_unlock_irqrestore(data->nvram_lock, irq_state);
480     }
481   
482     return 0;
483 }
484 */
485
486 static void nvram_update_timer(struct guest_info *vm,
487                                ullong_t           cpu_cycles,
488                                ullong_t           cpu_freq,
489                                void              *priv_data)
490 {
491     struct nvram_internal *nvram_state = (struct nvram_internal *)priv_data;
492     uint32_t period_us;
493
494     
495     period_us = (uint32_t) (1000000*cpu_cycles/cpu_freq);
496
497     update_time(nvram_state,period_us);
498
499 }
500
501
502 static void set_memory_size(struct nvram_internal * nvram, addr_t bytes) {
503     // 1. Conventional Mem: 0-640k in K
504     // 2. Extended Mem: 0-16MB in K
505     // 3. Big Mem: 0-4G in 64K
506
507     if (bytes > 640 * 1024) {
508         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, 0x02);
509         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, 0x80);
510
511         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_HIGH] = 0x02;
512         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_LOW] = 0x80;
513     } else {
514         uint16_t memk = bytes * 1024;
515         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, (memk >> 8) & 0x00ff);
516         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, memk & 0x00ff);
517
518         return;
519     }
520
521     if (bytes > (16 * 1024 * 1024)) {
522         // Set extended memory to 15 MB
523         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, 0x3C);
524         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, 0x00);
525         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, 0x3C);
526         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, 0x00);
527     } else {
528         uint16_t memk = bytes * 1024;
529
530         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, (memk >> 8) & 0x00ff);
531         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, memk & 0x00ff);
532         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, (memk >> 8) & 0x00ff);
533         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, memk & 0x00ff);
534
535         return;
536     }
537
538     {
539         // Set the extended memory beyond 16 MB in 64k chunks
540         uint16_t mem_chunks = (bytes - (1024 * 1024 * 16)) / (1024 * 64);
541
542         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_HIGH, (mem_chunks >> 8) & 0x00ff);
543         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_LOW, mem_chunks & 0x00ff);
544     }
545
546     return;
547 }
548
549
550
551 static void init_harddrives(struct nvram_internal * nvram) {
552     uint8_t hd_data = 0;
553     uint32_t cyls;
554     uint32_t sects;
555     uint32_t heads;
556     int i = 0;
557     int info_base_reg = 0x1b;
558     int type_reg = 0x19;
559
560     // 0x19 == first drive type
561     // 0x1a == second drive type
562
563     // 0x1b == first drive geometry base
564     // 0x24 == second drive geometry base
565
566     // It looks like the BIOS only tracks the disks on the first channel at 0x12?
567     for (i = 0; i < 2; i++) {
568         if (v3_ide_get_geometry(nvram->ide->private_data, 0, i, &cyls, &heads, &sects) == 0) {
569
570             int info_reg = info_base_reg + (i * 9);
571
572             set_memory(nvram, type_reg + i, 0x2f);
573
574             set_memory(nvram, info_reg, cyls & 0xff);
575             set_memory(nvram, info_reg + 1, (cyls >> 8) & 0xff);
576             set_memory(nvram, info_reg + 2, heads & 0xff);
577
578             // Write precomp cylinder (1 and 2)
579             set_memory(nvram, info_reg + 3, 0xff);
580             set_memory(nvram, info_reg + 4, 0xff);
581
582             // harddrive control byte 
583             set_memory(nvram, info_reg + 5, 0xc0 | ((heads > 8) << 3));
584
585             set_memory(nvram, info_reg + 6, cyls & 0xff);
586             set_memory(nvram, info_reg + 7, (cyls >> 8) & 0xff);
587
588             set_memory(nvram, info_reg + 8, sects & 0xff);
589             
590             hd_data |= (0xf0 >> (i * 4));
591         }
592     }
593
594     set_memory(nvram, NVRAM_IBM_HD_DATA, hd_data);
595     
596     {
597 #define TRANSLATE_NONE  0x0
598 #define TRANSLATE_LBA   0x1
599 #define TRANSLATE_LARGE 0x2
600 #define TRANSLATE_RECHS 0x3
601         // We're going to do LBA translation for everything...
602         uint8_t trans = 0;
603
604         for (i = 0; i < 4; i++) {
605             int chan_num = i / 2;
606             int drive_num = i % 2;
607             uint32_t tmp[3];
608
609             if (v3_ide_get_geometry(nvram->ide->private_data, chan_num, drive_num, &tmp[0], &tmp[1], &tmp[2]) == 0) {
610                 trans |= TRANSLATE_LBA << (i * 2);
611             }
612         }
613
614         set_memory(nvram, NVRAM_IDE_TRANSLATION, trans);
615     }
616 }
617
618 static uint16_t compute_checksum(struct nvram_internal * nvram) {
619     uint16_t checksum = 0;
620     uint8_t reg = 0;
621     uint8_t val = 0;
622     
623     /* add all fields between the RTC and the checksum fields */
624     for (reg = CHECKSUM_REGION_FIRST_BYTE; reg < CHECKSUM_REGION_LAST_BYTE; reg++) {
625         /* unset fields are considered zero so get_memory can be ignored */
626         get_memory(nvram, reg, &val);
627         checksum += val;
628     }
629                 
630     return checksum;
631 }
632
633 static int init_nvram_state(struct v3_vm_info * vm, struct nvram_internal * nvram) {
634     uint16_t checksum = 0;
635
636     memset(nvram->mem_state, 0, NVRAM_REG_MAX);
637     memset(nvram->reg_map, 0, NVRAM_REG_MAX / 8);
638
639     v3_lock_init(&(nvram->nvram_lock));
640
641     //
642     // 2 1.44 MB floppy drives
643     //
644 #if 1
645     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x44);
646 #else
647     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x00);
648 #endif
649
650     //
651     // For old boot sequence style, do floppy first
652     //
653     set_memory(nvram, NVRAM_REG_BOOTSEQ_OLD, 0x10);
654
655 #if 0
656     // For new boot sequence style, do floppy, cd, then hd
657     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x31);
658     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x20);
659 #endif
660
661     // For new boot sequence style, do cd, hd, floppy
662     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x23);
663     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x10);
664   
665   
666     // Set equipment byte to note 2 floppies, vga display, keyboard,math,floppy
667     set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0x4f);
668     // set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0xf);
669   
670
671     // Set the shutdown status gently
672     // soft reset
673     set_memory(nvram, NVRAM_REG_SHUTDOWN_STATUS, 0x0);
674
675
676     // RTC status A
677     // 00100110 = no update in progress, base=32768 Hz, rate = 1024 Hz
678     set_memory(nvram, NVRAM_REG_STAT_A, 0x26); 
679
680     // RTC status B
681     // 00000100 = not setting, no interrupts, blocked rect signal, bcd mode, 24 hour, normal time
682     set_memory(nvram, NVRAM_REG_STAT_B, 0x06); 
683
684
685     // RTC status C
686     // No IRQ requested, result not do to any source
687     set_memory(nvram, NVRAM_REG_STAT_C, 0x00);
688
689     // RTC status D
690     // Battery is OK
691     set_memory(nvram, NVRAM_REG_STAT_D, 0x80);
692
693
694     // january 1, 2008, 00:00:00
695     set_memory(nvram, NVRAM_REG_SEC, 0x00);
696     set_memory(nvram, NVRAM_REG_SEC_ALARM, 0x00);
697     set_memory(nvram, NVRAM_REG_MIN, 0x00);
698     set_memory(nvram, NVRAM_REG_MIN_ALARM, 0x00);
699     set_memory(nvram, NVRAM_REG_HOUR, 0x00);
700     set_memory(nvram, NVRAM_REG_HOUR_ALARM, 0x00);
701
702     set_memory(nvram, NVRAM_REG_MONTH, 0x01);
703     set_memory(nvram, NVRAM_REG_MONTH_DAY, 0x1);
704     set_memory(nvram, NVRAM_REG_WEEK_DAY, 0x1);
705     set_memory(nvram, NVRAM_REG_YEAR, 0x08);
706     set_memory(nvram, NVRAM_REG_IBM_CENTURY_BYTE, 0x20);
707     set_memory(nvram, NVRAM_REG_IBM_PS2_CENTURY_BYTE, 0x20);
708
709     set_memory(nvram, NVRAM_REG_DIAGNOSTIC_STATUS, 0x00);
710     
711     nvram->us = 0;
712     nvram->pus = 0;
713
714     set_memory_size(nvram, vm->mem_size);
715     init_harddrives(nvram);
716     
717     /* compute checksum (must follow all assignments here) */
718     checksum = compute_checksum(nvram);
719     set_memory(nvram, NVRAM_REG_CSUM_HIGH, (checksum >> 8) & 0xff);
720     set_memory(nvram, NVRAM_REG_CSUM_LOW, checksum & 0xff);
721
722     
723     
724     nvram->dev_state = NVRAM_READY;
725     nvram->thereg = 0;
726
727     return 0;
728 }
729
730
731
732
733
734
735 static int nvram_write_reg_port(struct guest_info * core, uint16_t port,
736                                 void * src, uint_t length, void * priv_data) {
737
738     struct nvram_internal * data = priv_data;
739     
740     memcpy(&(data->thereg), src, 1);
741     PrintDebug("Writing To NVRAM reg: 0x%x\n", data->thereg);
742
743     return 1;
744 }
745
746 static int nvram_read_data_port(struct guest_info * core, uint16_t port,
747                                 void * dst, uint_t length, void * priv_data) {
748
749     struct nvram_internal * data = priv_data;
750
751     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
752
753     if (get_memory(data, data->thereg, (uint8_t *)dst) == -1) {
754         PrintError("Register %d (0x%x) Not set\n", data->thereg, data->thereg);
755
756         v3_unlock_irqrestore(data->nvram_lock, irq_state);
757
758         /* allow guest to query checksummed bytes; warn but read zero rather than fail in this case */
759         if ((data->thereg >= CHECKSUM_REGION_FIRST_BYTE) && (data->thereg <= CHECKSUM_REGION_LAST_BYTE)) {
760             return 1;
761         } else {        
762             return -1;
763         }
764     }
765
766     PrintDebug("nvram_read_data_port(0x%x)  =  0x%x\n", data->thereg, *(uint8_t *)dst);
767
768     // hack
769     if (data->thereg == NVRAM_REG_STAT_A) { 
770         data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
771     }
772
773     v3_unlock_irqrestore(data->nvram_lock, irq_state);
774
775     return 1;
776 }
777
778
779 static int nvram_write_data_port(struct guest_info * core, uint16_t port,
780                                  void * src, uint_t length, void * priv_data) {
781
782     struct nvram_internal * data = priv_data;
783
784     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
785
786     set_memory(data, data->thereg, *(uint8_t *)src);
787
788     v3_unlock_irqrestore(data->nvram_lock, irq_state);
789
790     PrintDebug("nvram_write_data_port(0x%x) = 0x%x\n", 
791                data->thereg, data->mem_state[data->thereg]);
792
793     return 1;
794 }
795
796
797
798
799 static int nvram_free(struct nvram_internal * nvram_state) {
800     
801     // unregister host events
802     struct guest_info *info = &(nvram_state->vm->cores[0]);
803
804     if (nvram_state->timer) { 
805         v3_remove_timer(info,nvram_state->timer);
806     }
807
808     V3_Free(nvram_state);
809     return 0;
810 }
811
812
813
814 static struct v3_timer_ops timer_ops = {
815     .update_timer = nvram_update_timer,
816 };
817
818
819 static struct v3_device_ops dev_ops = {  
820     .free = (int (*)(void *))nvram_free,
821 };
822
823
824
825
826
827 static int nvram_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
828     struct nvram_internal * nvram_state = NULL;
829     struct vm_device * ide = v3_find_dev(vm, v3_cfg_val(cfg, "storage"));
830     char * dev_id = v3_cfg_val(cfg, "ID");
831     int ret = 0;
832
833     if (!ide) {
834         PrintError("Could not find IDE device\n");
835         return -1;
836     }
837
838     PrintDebug("nvram: init_device\n");
839     nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
840
841     PrintDebug("nvram: internal at %p\n", (void *)nvram_state);
842
843     nvram_state->ide = ide;
844     nvram_state->vm = vm;
845
846     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nvram_state);
847
848     if (dev == NULL) {
849         PrintError("Could not attach device %s\n", dev_id);
850         V3_Free(nvram_state);
851         return -1;
852     }
853
854     init_nvram_state(vm, nvram_state);
855
856     // hook ports
857     ret |= v3_dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
858     ret |= v3_dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
859   
860     if (ret != 0) {
861         PrintError("Error hooking NVRAM IO ports\n");
862         v3_remove_device(dev);
863         return -1;
864     }
865
866     //    v3_hook_host_event(vm, HOST_TIMER_EVT, V3_HOST_EVENT_HANDLER(handle_timer_event), nvram_state);
867     nvram_state->timer = v3_add_timer(&(vm->cores[0]),&timer_ops,nvram_state);
868
869     if (nvram_state->timer == NULL ) { 
870         v3_remove_device(dev);
871         return -1;
872     }
873
874     return 0;
875 }
876
877 device_register("NVRAM", nvram_init)