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.


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