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.


removed old extension files
[palacios-OLD.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 V3_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     uint64_t        us;   //microseconds - for clock update - zeroed every second
117     uint64_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;  // 0=date/time is in bcd, 1=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, uint64_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 == 0);
301     uint8_t carry = 0;
302     uint8_t nextday = 0;
303     uint32_t  periodic_period;
304
305     PrintDebug("nvram: update_time by %llu microseocnds\n",period_us);
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             PrintError("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                 PrintError("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                     PrintError("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 void nvram_update_timer(struct guest_info *vm,
467                                ullong_t           cpu_cycles,
468                                ullong_t           cpu_freq,
469                                void              *priv_data)
470 {
471     struct nvram_internal *nvram_state = (struct nvram_internal *)priv_data;
472     uint64_t period_us;
473
474     
475     // cpu freq in khz
476     period_us = (1000*cpu_cycles/cpu_freq);
477
478     update_time(nvram_state,period_us);
479
480 }
481
482
483 static void set_memory_size(struct nvram_internal * nvram, addr_t bytes) {
484     // 1. Conventional Mem: 0-640k in K
485     // 2. Extended Mem: 0-16MB in K
486     // 3. Big Mem: 0-4G in 64K
487
488     if (bytes > 640 * 1024) {
489         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, 0x02);
490         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, 0x80);
491
492         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_HIGH] = 0x02;
493         //      nvram->mem_state[NVRAM_REG_BASE_MEMORY_LOW] = 0x80;
494     } else {
495         uint16_t memk = bytes * 1024;
496         set_memory(nvram, NVRAM_REG_BASE_MEMORY_HIGH, (memk >> 8) & 0x00ff);
497         set_memory(nvram, NVRAM_REG_BASE_MEMORY_LOW, memk & 0x00ff);
498
499         return;
500     }
501
502     if (bytes > (16 * 1024 * 1024)) {
503         // Set extended memory to 15 MB
504         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, 0x3C);
505         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, 0x00);
506         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, 0x3C);
507         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, 0x00);
508     } else {
509         uint16_t memk = bytes * 1024;
510
511         set_memory(nvram, NVRAM_REG_EXT_MEMORY_HIGH, (memk >> 8) & 0x00ff);
512         set_memory(nvram, NVRAM_REG_EXT_MEMORY_LOW, memk & 0x00ff);
513         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_HIGH, (memk >> 8) & 0x00ff);
514         set_memory(nvram, NVRAM_REG_EXT_MEMORY_2ND_LOW, memk & 0x00ff);
515
516         return;
517     }
518
519     {
520         // Set the extended memory beyond 16 MB in 64k chunks
521         uint16_t mem_chunks = (bytes - (1024 * 1024 * 16)) / (1024 * 64);
522
523         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_HIGH, (mem_chunks >> 8) & 0x00ff);
524         set_memory(nvram, NVRAM_REG_AMI_BIG_MEMORY_LOW, mem_chunks & 0x00ff);
525     }
526
527     return;
528 }
529
530
531
532 static void init_harddrives(struct nvram_internal * nvram) {
533     uint8_t hd_data = 0;
534     uint32_t cyls;
535     uint32_t sects;
536     uint32_t heads;
537     int i = 0;
538     int info_base_reg = 0x1b;
539     int type_reg = 0x19;
540
541     // 0x19 == first drive type
542     // 0x1a == second drive type
543
544     // 0x1b == first drive geometry base
545     // 0x24 == second drive geometry base
546
547     // It looks like the BIOS only tracks the disks on the first channel at 0x12?
548     for (i = 0; i < 2; i++) {
549         if (v3_ide_get_geometry(nvram->ide->private_data, 0, i, &cyls, &heads, &sects) == 0) {
550
551             int info_reg = info_base_reg + (i * 9);
552
553             set_memory(nvram, type_reg + i, 0x2f);
554
555             set_memory(nvram, info_reg, cyls & 0xff);
556             set_memory(nvram, info_reg + 1, (cyls >> 8) & 0xff);
557             set_memory(nvram, info_reg + 2, heads & 0xff);
558
559             // Write precomp cylinder (1 and 2)
560             set_memory(nvram, info_reg + 3, 0xff);
561             set_memory(nvram, info_reg + 4, 0xff);
562
563             // harddrive control byte 
564             set_memory(nvram, info_reg + 5, 0xc0 | ((heads > 8) << 3));
565
566             set_memory(nvram, info_reg + 6, cyls & 0xff);
567             set_memory(nvram, info_reg + 7, (cyls >> 8) & 0xff);
568
569             set_memory(nvram, info_reg + 8, sects & 0xff);
570             
571             hd_data |= (0xf0 >> (i * 4));
572         }
573     }
574
575     set_memory(nvram, NVRAM_IBM_HD_DATA, hd_data);
576     
577     {
578 #define TRANSLATE_NONE  0x0
579 #define TRANSLATE_LBA   0x1
580 #define TRANSLATE_LARGE 0x2
581 #define TRANSLATE_RECHS 0x3
582         // We're going to do LBA translation for everything...
583         uint8_t trans = 0;
584
585         for (i = 0; i < 4; i++) {
586             int chan_num = i / 2;
587             int drive_num = i % 2;
588             uint32_t tmp[3];
589
590             if (v3_ide_get_geometry(nvram->ide->private_data, chan_num, drive_num, &tmp[0], &tmp[1], &tmp[2]) == 0) {
591                 trans |= TRANSLATE_LBA << (i * 2);
592             }
593         }
594
595         set_memory(nvram, NVRAM_IDE_TRANSLATION, trans);
596     }
597 }
598
599 static uint16_t compute_checksum(struct nvram_internal * nvram) {
600     uint16_t checksum = 0;
601     uint8_t reg = 0;
602     uint8_t val = 0;
603     
604     /* add all fields between the RTC and the checksum fields */
605     for (reg = CHECKSUM_REGION_FIRST_BYTE; reg < CHECKSUM_REGION_LAST_BYTE; reg++) {
606         /* unset fields are considered zero so get_memory can be ignored */
607         get_memory(nvram, reg, &val);
608         checksum += val;
609     }
610                 
611     return checksum;
612 }
613
614 static int init_nvram_state(struct v3_vm_info * vm, struct nvram_internal * nvram) {
615     uint16_t checksum = 0;
616
617     memset(nvram->mem_state, 0, NVRAM_REG_MAX);
618     memset(nvram->reg_map, 0, NVRAM_REG_MAX / 8);
619
620     v3_lock_init(&(nvram->nvram_lock));
621
622     //
623     // 2 1.44 MB floppy drives
624     //
625 #if 1
626     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x44);
627 #else
628     set_memory(nvram, NVRAM_REG_FLOPPY_TYPE, 0x00);
629 #endif
630
631     //
632     // For old boot sequence style, do floppy first
633     //
634     set_memory(nvram, NVRAM_REG_BOOTSEQ_OLD, 0x10);
635
636 #if 0
637     // For new boot sequence style, do floppy, cd, then hd
638     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x31);
639     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x20);
640 #endif
641
642     // For new boot sequence style, do cd, hd, floppy
643     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_FIRST, 0x23);
644     set_memory(nvram, NVRAM_REG_BOOTSEQ_NEW_SECOND, 0x10);
645   
646   
647     // Set equipment byte to note 2 floppies, vga display, keyboard,math,floppy
648     set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0x4f);
649     // set_memory(nvram, NVRAM_REG_EQUIPMENT_BYTE, 0xf);
650   
651
652     // Set the shutdown status gently
653     // soft reset
654     set_memory(nvram, NVRAM_REG_SHUTDOWN_STATUS, 0x0);
655
656
657     // RTC status A
658     // 00100110 = no update in progress, base=32768 Hz, rate = 1024 Hz
659     set_memory(nvram, NVRAM_REG_STAT_A, 0x26); 
660
661     // RTC status B
662     // 00000010 = not setting, no interrupts, blocked rect signal, bcd mode (bit 3 = 0), 24 hour, normal time
663     set_memory(nvram, NVRAM_REG_STAT_B, 0x02); 
664
665
666     // RTC status C
667     // No IRQ requested, result not do to any source
668     set_memory(nvram, NVRAM_REG_STAT_C, 0x00);
669
670     // RTC status D
671     // Battery is OK
672     set_memory(nvram, NVRAM_REG_STAT_D, 0x80);
673
674
675     // january 1, 2008, 00:00:00
676     set_memory(nvram, NVRAM_REG_SEC, 0x00);
677     set_memory(nvram, NVRAM_REG_SEC_ALARM, 0x00);
678     set_memory(nvram, NVRAM_REG_MIN, 0x00);
679     set_memory(nvram, NVRAM_REG_MIN_ALARM, 0x00);
680     set_memory(nvram, NVRAM_REG_HOUR, 0x00);
681     set_memory(nvram, NVRAM_REG_HOUR_ALARM, 0x00);
682
683     set_memory(nvram, NVRAM_REG_MONTH, 0x01);
684     set_memory(nvram, NVRAM_REG_MONTH_DAY, 0x1);
685     set_memory(nvram, NVRAM_REG_WEEK_DAY, 0x1);
686     set_memory(nvram, NVRAM_REG_YEAR, 0x08);
687     set_memory(nvram, NVRAM_REG_IBM_CENTURY_BYTE, 0x20);
688     set_memory(nvram, NVRAM_REG_IBM_PS2_CENTURY_BYTE, 0x20);
689
690     set_memory(nvram, NVRAM_REG_DIAGNOSTIC_STATUS, 0x00);
691     
692     nvram->us = 0;
693     nvram->pus = 0;
694
695     set_memory_size(nvram, vm->mem_size);
696     init_harddrives(nvram);
697     
698     /* compute checksum (must follow all assignments here) */
699     checksum = compute_checksum(nvram);
700     set_memory(nvram, NVRAM_REG_CSUM_HIGH, (checksum >> 8) & 0xff);
701     set_memory(nvram, NVRAM_REG_CSUM_LOW, checksum & 0xff);
702
703     
704     
705     nvram->dev_state = NVRAM_READY;
706     nvram->thereg = 0;
707
708     return 0;
709 }
710
711
712
713
714
715
716 static int nvram_write_reg_port(struct guest_info * core, uint16_t port,
717                                 void * src, uint_t length, void * priv_data) {
718
719     struct nvram_internal * data = priv_data;
720     
721     memcpy(&(data->thereg), src, 1);
722     PrintDebug("nvram: Writing To NVRAM reg: 0x%x\n", data->thereg);
723
724     return 1;
725 }
726
727 static int nvram_read_data_port(struct guest_info * core, uint16_t port,
728                                 void * dst, uint_t length, void * priv_data) {
729
730     struct nvram_internal * data = priv_data;
731
732     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
733
734     if (get_memory(data, data->thereg, (uint8_t *)dst) == -1) {
735         PrintError("nvram: Register %d (0x%x) Not set\n", data->thereg, data->thereg);
736
737         v3_unlock_irqrestore(data->nvram_lock, irq_state);
738
739         /* allow guest to query checksummed bytes; warn but read zero rather than fail in this case */
740         if ((data->thereg >= CHECKSUM_REGION_FIRST_BYTE) && (data->thereg <= CHECKSUM_REGION_LAST_BYTE)) {
741             return 1;
742         } else {        
743             return -1;
744         }
745     }
746
747     PrintDebug("nvram: nvram_read_data_port(0x%x)  =  0x%x\n", data->thereg, *(uint8_t *)dst);
748
749     // hack
750     if (data->thereg == NVRAM_REG_STAT_A) { 
751         data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
752     }
753
754     v3_unlock_irqrestore(data->nvram_lock, irq_state);
755
756     return 1;
757 }
758
759
760 static int nvram_write_data_port(struct guest_info * core, uint16_t port,
761                                  void * src, uint_t length, void * priv_data) {
762
763     struct nvram_internal * data = priv_data;
764
765     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
766
767     set_memory(data, data->thereg, *(uint8_t *)src);
768
769     v3_unlock_irqrestore(data->nvram_lock, irq_state);
770
771     PrintDebug("nvram: nvram_write_data_port(0x%x) = 0x%x\n", 
772                data->thereg, data->mem_state[data->thereg]);
773
774     return 1;
775 }
776
777
778
779
780 static int nvram_free(struct nvram_internal * nvram_state) {
781     
782     // unregister host events
783     struct guest_info *info = &(nvram_state->vm->cores[0]);
784
785     if (nvram_state->timer) { 
786         v3_remove_timer(info,nvram_state->timer);
787     }
788
789     V3_Free(nvram_state);
790     return 0;
791 }
792
793
794
795 static struct v3_timer_ops timer_ops = {
796     .update_timer = nvram_update_timer,
797 };
798
799
800 static struct v3_device_ops dev_ops = {  
801     .free = (int (*)(void *))nvram_free,
802 };
803
804
805
806
807
808 static int nvram_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
809     struct nvram_internal * nvram_state = NULL;
810     struct vm_device * ide = v3_find_dev(vm, v3_cfg_val(cfg, "storage"));
811     char * dev_id = v3_cfg_val(cfg, "ID");
812     int ret = 0;
813
814     if (!ide) {
815         PrintError("nvram: Could not find IDE device\n");
816         return -1;
817     }
818
819     PrintDebug("nvram: init_device\n");
820     nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
821
822     PrintDebug("nvram: internal at %p\n", (void *)nvram_state);
823
824     nvram_state->ide = ide;
825     nvram_state->vm = vm;
826
827     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nvram_state);
828
829     if (dev == NULL) {
830         PrintError("nvram: Could not attach device %s\n", dev_id);
831         V3_Free(nvram_state);
832         return -1;
833     }
834
835     init_nvram_state(vm, nvram_state);
836
837     // hook ports
838     ret |= v3_dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
839     ret |= v3_dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
840   
841     if (ret != 0) {
842         PrintError("nvram: Error hooking NVRAM IO ports\n");
843         v3_remove_device(dev);
844         return -1;
845     }
846
847     nvram_state->timer = v3_add_timer(&(vm->cores[0]),&timer_ops,nvram_state);
848
849     if (nvram_state->timer == NULL ) { 
850         v3_remove_device(dev);
851         return -1;
852     }
853
854     return 0;
855 }
856
857 device_register("NVRAM", nvram_init)