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.


fixed dest_apic determination
[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 * cent_ps2 = (uint8_t *)&(data->mem_state[NVRAM_REG_IBM_PS2_CENTURY_BYTE]);
292     uint8_t * seca     = (uint8_t *)&(data->mem_state[NVRAM_REG_SEC_ALARM]);
293     uint8_t * mina     = (uint8_t *)&(data->mem_state[NVRAM_REG_MIN_ALARM]);
294     uint8_t * houra    = (uint8_t *)&(data->mem_state[NVRAM_REG_HOUR_ALARM]);
295     uint8_t hour24;
296
297     uint8_t bcd = (statb->dm == 1);
298     uint8_t carry = 0;
299     uint8_t nextday = 0;
300     uint32_t  periodic_period;
301
302     //PrintDebug("nvram: sizeof(struct rtc_stata)=%d\n", sizeof(struct rtc_stata));
303
304
305     //PrintDebug("nvram: update_time\n",statb->pi);
306   
307     // We will set these flags on exit
308     statc->irq = 0;
309     statc->pf = 0;
310     statc->af = 0;
311     statc->uf = 0;
312
313     // We will reset us after one second
314     data->us += period_us;
315     // We will reset pus after one periodic_period
316     data->pus += period_us;
317
318     if (data->us > 1000000) { 
319         carry = 1;
320         carry = add_to(sec, &carry, bcd);
321
322         if (carry) { 
323             PrintDebug("nvram: somehow managed to get a carry in second update\n"); 
324         }
325
326         if ( (bcd && (*sec == 0x60)) || 
327              ((!bcd) && (*sec == 60))) { 
328   
329             *sec = 0;
330
331             carry = 1;
332             carry = add_to(min, &carry, bcd);
333             if (carry) { 
334                 PrintDebug("nvram: somehow managed to get a carry in minute update\n"); 
335             }
336
337             if ( (bcd && (*min == 0x60)) || 
338                  ((!bcd) && (*min == 60))) { 
339
340                 *min = 0;
341                 hour24 = *hour;
342
343                 if (!(statb->h24)) { 
344
345                     if (hour24 & 0x80) { 
346                         hour24 &= 0x8f;
347                         uint8_t temp = ((bcd) ? 0x12 : 12);
348                         add_to(&hour24, &temp, bcd);
349                     }
350                 }
351
352                 carry = 1;
353                 carry = add_to(&hour24, &carry, bcd);
354                 if (carry) { 
355                     PrintDebug("nvram: somehow managed to get a carry in hour update\n"); 
356                 }
357
358                 if ( (bcd && (hour24 == 0x24)) || 
359                      ((!bcd) && (hour24 == 24))) { 
360                     carry = 1;
361                     nextday = 1;
362                     hour24 = 0;
363                 } else {
364                     carry = 0;
365                 }
366
367
368                 if (statb->h24) { 
369                     *hour = hour24;
370                 } else {
371                     if ( (bcd && (hour24 < 0x12)) || 
372                          ((!bcd) && (hour24 < 12))) { 
373                         *hour = hour24;
374
375                     } else {
376
377                         if (!bcd) { 
378                             *hour = (hour24 - 12) | 0x80;
379                         } else {
380                             *hour = hour24;
381                             struct bcd_num * n = (struct bcd_num *)hour;
382
383                             if (n->bot < 0x2) { 
384                                 n->top--;
385                                 n->bot += 0xa;
386                             }
387
388                             n->bot -= 0x2;
389                             n->top -= 0x1;
390                         }
391                     }
392                 }
393
394                 // now see if we need to carry into the days and further
395                 if (nextday) { 
396                     carry = 1;
397                     add_to(weekday, &carry, bcd);
398
399                     *weekday %= 0x7;  // same regardless of bcd
400
401                     if ((*monthday) != days_in_month(*month, bcd)) {
402                         add_to(monthday, &carry, bcd);
403                     } else {
404                         *monthday = 0x1;
405
406                         carry = 1;
407                         add_to(month, &carry, bcd);
408
409                         if ( (bcd && (*month == 0x13)) || 
410                              ((!bcd) && (*month == 13))) { 
411                             *month = 1; // same for both 
412
413                             carry = 1;
414                             carry = add_to(year, &carry, bcd);
415
416                             if ( (bcd && carry) || 
417                                  ((!bcd) && (*year == 100))) { 
418                                 *year = 0;
419                                 carry = 1;
420                                 add_to(cent, &carry, bcd);
421                                 *cent_ps2 = *cent;
422                             }
423                         }
424                     }
425                 }
426             }
427         }
428
429
430         data->us -= 1000000;
431         // OK, now check for the alarm, if it is set to interrupt
432         if (statb->ai) { 
433             if ((*sec == *seca) && (*min == *mina) && (*hour == *houra)) { 
434                 statc->af = 1;
435                 PrintDebug("nvram: interrupt on alarm\n");
436             }
437         }
438     }
439
440     if (statb->pi) { 
441         periodic_period = 1000000 / (65536 / (0x1 << stata->rate));
442         if (data->pus >= periodic_period) { 
443             statc->pf = 1;
444             data->pus -= periodic_period;
445             PrintDebug("nvram: interrupt on periodic\n");
446         }
447     }
448
449     if (statb->ui) { 
450         statc->uf = 1;
451         PrintDebug("nvram: interrupt on update\n");
452     }
453
454     statc->irq = (statc->pf || statc->af || statc->uf);
455   
456     //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);
457   
458     // Interrupt associated VM, if needed
459     if (statc->irq) { 
460         PrintDebug("nvram: injecting interrupt\n");
461         v3_raise_irq(data->vm, NVRAM_RTC_IRQ);
462     }
463 }
464
465
466 static int handle_timer_event(struct v3_vm_info * vm, 
467                               struct v3_timer_event * evt, 
468                               void * priv_data) {
469
470
471     struct nvram_internal * data = priv_data;
472
473     if (data) {
474         addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
475         update_time(data, evt->period_us);
476         v3_unlock_irqrestore(data->nvram_lock, irq_state);
477     }
478   
479     return 0;
480 }
481
482
483
484 static void set_memory_size(struct nvram_internal * nvram, addr_t bytes) {
485     // 1. Conventional Mem: 0-640k in K
486     // 2. Extended Mem: 0-16MB in K
487     // 3. Big Mem: 0-4G in 64K
488
489     if (bytes > 640 * 1024) {
490         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, 0x02);
491         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, 0x80);
492
493         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_HIGH] = 0x02;
494         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_LOW] = 0x80;
495     } else {
496         uint16_t memk = bytes * 1024;
497         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, (memk >> 8) & 0x00ff);
498         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, memk & 0x00ff);
499
500         return;
501     }
502
503     if (bytes > (16 * 1024 * 1024)) {
504         // Set extended memory to 15 MB
505         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, 0x3C);
506         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, 0x00);
507         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, 0x3C);
508         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, 0x00);
509     } else {
510         uint16_t memk = bytes * 1024;
511
512         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, (memk >> 8) & 0x00ff);
513         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, memk & 0x00ff);
514         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, (memk >> 8) & 0x00ff);
515         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, memk & 0x00ff);
516
517         return;
518     }
519
520     {
521         // Set the extended memory beyond 16 MB in 64k chunks
522         uint16_t mem_chunks = (bytes - (1024 * 1024 * 16)) / (1024 * 64);
523
524         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_HIGH, (mem_chunks >> 8) & 0x00ff);
525         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_LOW, mem_chunks & 0x00ff);
526     }
527
528     return;
529 }
530
531
532
533 static void init_harddrives(struct nvram_internal * nvram) {
534     uint8_t hd_data = 0;
535     uint32_t cyls;
536     uint32_t sects;
537     uint32_t heads;
538     int i = 0;
539     int info_base_reg = 0x1b;
540     int type_reg = 0x19;
541
542     // 0x19 == first drive type
543     // 0x1a == second drive type
544
545     // 0x1b == first drive geometry base
546     // 0x24 == second drive geometry base
547
548     // It looks like the BIOS only tracks the disks on the first channel at 0x12?
549     for (i = 0; i < 2; i++) {
550         if (v3_ide_get_geometry(nvram->ide->private_data, 0, i, &cyls, &heads, &sects) == 0) {
551
552             int info_reg = info_base_reg + (i * 9);
553
554             set_memory(nvram, type_reg + i, 0x2f);
555
556             set_memory(nvram, info_reg, cyls & 0xff);
557             set_memory(nvram, info_reg + 1, (cyls >> 8) & 0xff);
558             set_memory(nvram, info_reg + 2, heads & 0xff);
559
560             // Write precomp cylinder (1 and 2)
561             set_memory(nvram, info_reg + 3, 0xff);
562             set_memory(nvram, info_reg + 4, 0xff);
563
564             // harddrive control byte 
565             set_memory(nvram, info_reg + 5, 0xc0 | ((heads > 8) << 3));
566
567             set_memory(nvram, info_reg + 6, cyls & 0xff);
568             set_memory(nvram, info_reg + 7, (cyls >> 8) & 0xff);
569
570             set_memory(nvram, info_reg + 8, sects & 0xff);
571             
572             hd_data |= (0xf0 >> (i * 4));
573         }
574     }
575
576     set_memory(nvram, NVRAM_IBM_HD_DATA, hd_data);
577     
578     {
579 #define TRANSLATE_NONE  0x0
580 #define TRANSLATE_LBA   0x1
581 #define TRANSLATE_LARGE 0x2
582 #define TRANSLATE_RECHS 0x3
583         // We're going to do LBA translation for everything...
584         uint8_t trans = 0;
585
586         for (i = 0; i < 4; i++) {
587             int chan_num = i / 2;
588             int drive_num = i % 2;
589             uint32_t tmp[3];
590
591             if (v3_ide_get_geometry(nvram->ide->private_data, chan_num, drive_num, &tmp[0], &tmp[1], &tmp[2]) == 0) {
592                 trans |= TRANSLATE_LBA << (i * 2);
593             }
594         }
595
596         set_memory(nvram, NVRAM_IDE_TRANSLATION, trans);
597     }
598 }
599
600 static uint16_t compute_checksum(struct nvram_internal * nvram) {
601     uint16_t checksum = 0;
602     uint8_t reg = 0;
603     uint8_t val = 0;
604     
605     /* add all fields between the RTC and the checksum fields */
606     for (reg = CHECKSUM_REGION_FIRST_BYTE; reg < CHECKSUM_REGION_LAST_BYTE; reg++) {
607         /* unset fields are considered zero so get_memory can be ignored */
608         get_memory(nvram, reg, &val);
609         checksum += val;
610     }
611                 
612     return checksum;
613 }
614
615 static int init_nvram_state(struct v3_vm_info * vm, struct nvram_internal * nvram) {
616     uint16_t checksum = 0;
617
618     memset(nvram->mem_state, 0, NVRAM_REG_MAX);
619     memset(nvram->reg_map, 0, NVRAM_REG_MAX / 8);
620
621     v3_lock_init(&(nvram->nvram_lock));
622
623     //
624     // 2 1.44 MB floppy drives
625     //
626 #if 1
627     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x44);
628 #else
629     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x00);
630 #endif
631
632     //
633     // For old boot sequence style, do floppy first
634     //
635     set_memory(nvram, NVRAM_REG_BOOTSEQ_OLD, 0x10);
636
637 #if 0
638     // For new boot sequence style, do floppy, cd, then hd
639     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x31);
640     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x20);
641 #endif
642
643     // For new boot sequence style, do cd, hd, floppy
644     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x23);
645     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x10);
646   
647   
648     // Set equipment byte to note 2 floppies, vga display, keyboard,math,floppy
649     set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0x4f);
650     // set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0xf);
651   
652
653     // Set the shutdown status gently
654     // soft reset
655     set_memory(nvram, NVRAM_REG_SHUTDOWN_STATUS, 0x0);
656
657
658     // RTC status A
659     // 00100110 = no update in progress, base=32768 Hz, rate = 1024 Hz
660     set_memory(nvram, NVRAM_REG_STAT_A, 0x26); 
661
662     // RTC status B
663     // 00000100 = not setting, no interrupts, blocked rect signal, bcd mode, 24 hour, normal time
664     set_memory(nvram, NVRAM_REG_STAT_B, 0x06); 
665
666
667     // RTC status C
668     // No IRQ requested, result not do to any source
669     set_memory(nvram, NVRAM_REG_STAT_C, 0x00);
670
671     // RTC status D
672     // Battery is OK
673     set_memory(nvram, NVRAM_REG_STAT_D, 0x80);
674
675
676     // january 1, 2008, 00:00:00
677     set_memory(nvram, NVRAM_REG_SEC, 0x00);
678     set_memory(nvram, NVRAM_REG_SEC_ALARM, 0x00);
679     set_memory(nvram, NVRAM_REG_MIN, 0x00);
680     set_memory(nvram, NVRAM_REG_MIN_ALARM, 0x00);
681     set_memory(nvram, NVRAM_REG_HOUR, 0x00);
682     set_memory(nvram, NVRAM_REG_HOUR_ALARM, 0x00);
683
684     set_memory(nvram, NVRAM_REG_MONTH, 0x01);
685     set_memory(nvram, NVRAM_REG_MONTH_DAY, 0x1);
686     set_memory(nvram, NVRAM_REG_WEEK_DAY, 0x1);
687     set_memory(nvram, NVRAM_REG_YEAR, 0x08);
688     set_memory(nvram, NVRAM_REG_IBM_CENTURY_BYTE, 0x20);
689     set_memory(nvram, NVRAM_REG_IBM_PS2_CENTURY_BYTE, 0x20);
690
691     set_memory(nvram, NVRAM_REG_DIAGNOSTIC_STATUS, 0x00);
692     
693     nvram->us = 0;
694     nvram->pus = 0;
695
696     set_memory_size(nvram, vm->mem_size);
697     init_harddrives(nvram);
698     
699     /* compute checksum (must follow all assignments here) */
700     checksum = compute_checksum(nvram);
701     set_memory(nvram, NVRAM_REG_CSUM_HIGH, (checksum >> 8) & 0xff);
702     set_memory(nvram, NVRAM_REG_CSUM_LOW, checksum & 0xff);
703
704     
705     
706     nvram->dev_state = NVRAM_READY;
707     nvram->thereg = 0;
708
709     return 0;
710 }
711
712
713
714
715
716
717 static int nvram_write_reg_port(struct guest_info * core, uint16_t port,
718                                 void * src, uint_t length, void * priv_data) {
719
720     struct nvram_internal * data = priv_data;
721     
722     memcpy(&(data->thereg), src, 1);
723     PrintDebug("Writing To NVRAM reg: 0x%x\n", data->thereg);
724
725     return 1;
726 }
727
728 static int nvram_read_data_port(struct guest_info * core, uint16_t port,
729                                 void * dst, uint_t length, void * priv_data) {
730
731     struct nvram_internal * data = priv_data;
732
733     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
734
735     if (get_memory(data, data->thereg, (uint8_t *)dst) == -1) {
736         PrintError("Register %d (0x%x) Not set\n", data->thereg, data->thereg);
737
738         v3_unlock_irqrestore(data->nvram_lock, irq_state);
739
740         /* allow guest to query checksummed bytes; warn but read zero rather than fail in this case */
741         if ((data->thereg >= CHECKSUM_REGION_FIRST_BYTE) && (data->thereg <= CHECKSUM_REGION_LAST_BYTE)) {
742             return 1;
743         } else {        
744             return -1;
745         }
746     }
747
748     PrintDebug("nvram_read_data_port(0x%x)  =  0x%x\n", data->thereg, *(uint8_t *)dst);
749
750     // hack
751     if (data->thereg == NVRAM_REG_STAT_A) { 
752         data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
753     }
754
755     v3_unlock_irqrestore(data->nvram_lock, irq_state);
756
757     return 1;
758 }
759
760
761 static int nvram_write_data_port(struct guest_info * core, uint16_t port,
762                                  void * src, uint_t length, void * priv_data) {
763
764     struct nvram_internal * data = priv_data;
765
766     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
767
768     set_memory(data, data->thereg, *(uint8_t *)src);
769
770     v3_unlock_irqrestore(data->nvram_lock, irq_state);
771
772     PrintDebug("nvram_write_data_port(0x%x) = 0x%x\n", 
773                data->thereg, data->mem_state[data->thereg]);
774
775     return 1;
776 }
777
778
779
780
781 static int nvram_free(struct nvram_internal * nvram_state) {
782
783     // unregister host events
784
785     V3_Free(nvram_state);
786     return 0;
787 }
788
789
790
791
792
793 static struct v3_device_ops dev_ops = {  
794     .free = (int (*)(void *))nvram_free,
795 };
796
797
798
799
800
801 static int nvram_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
802     struct nvram_internal * nvram_state = NULL;
803     struct vm_device * ide = v3_find_dev(vm, v3_cfg_val(cfg, "storage"));
804     char * dev_id = v3_cfg_val(cfg, "ID");
805     int ret = 0;
806
807     if (!ide) {
808         PrintError("Could not find IDE device\n");
809         return -1;
810     }
811
812     PrintDebug("nvram: init_device\n");
813     nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
814
815     PrintDebug("nvram: internal at %p\n", (void *)nvram_state);
816
817     nvram_state->ide = ide;
818     nvram_state->vm = vm;
819
820     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nvram_state);
821
822     if (dev == NULL) {
823         PrintError("Could not attach device %s\n", dev_id);
824         V3_Free(nvram_state);
825         return -1;
826     }
827
828     init_nvram_state(vm, nvram_state);
829
830     // hook ports
831     ret |= v3_dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
832     ret |= v3_dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
833   
834     if (ret != 0) {
835         PrintError("Error hooking NVRAM IO ports\n");
836         v3_remove_device(dev);
837         return -1;
838     }
839
840     v3_hook_host_event(vm, HOST_TIMER_EVT, V3_HOST_EVENT_HANDLER(handle_timer_event), nvram_state);
841
842     return 0;
843 }
844
845 device_register("NVRAM", nvram_init)