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.


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