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.


f2c45a386a738c1157feea2394a8033d171156d1
[palacios.git] / palacios / src / devices / nvram.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Peter Dinda <pdinda@northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20
21 #include <palacios/vmm_dev_mgr.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_types.h>
24
25 #include <palacios/vmm_lock.h>
26
27 #include <devices/ide.h>
28 #include <palacios/vmm_intr.h>
29 #include <palacios/vmm_host_events.h>
30 #include <palacios/vm_guest.h>
31
32
33 #ifndef CONFIG_DEBUG_NVRAM
34 #undef PrintDebug
35 #define PrintDebug(fmt, args...)
36 #endif
37
38
39 #define NVRAM_REG_PORT  0x70
40 #define NVRAM_DATA_PORT 0x71
41
42 #define NVRAM_RTC_IRQ   0x8
43
44
45 typedef enum {NVRAM_READY, NVRAM_REG_POSTED} nvram_state_t;
46
47
48 #define NVRAM_REG_MAX   256
49
50
51 // These are borrowed from Bochs, which borrowed from
52 // Ralf Brown's interupt list, and extended
53 #define NVRAM_REG_SEC                     0x00
54 #define NVRAM_REG_SEC_ALARM               0x01
55 #define NVRAM_REG_MIN                     0x02
56 #define NVRAM_REG_MIN_ALARM               0x03
57 #define NVRAM_REG_HOUR                    0x04
58 #define NVRAM_REG_HOUR_ALARM              0x05
59 #define NVRAM_REG_WEEK_DAY                0x06
60 #define NVRAM_REG_MONTH_DAY               0x07
61 #define NVRAM_REG_MONTH                   0x08
62 #define NVRAM_REG_YEAR                    0x09
63 #define NVRAM_REG_STAT_A                  0x0a
64 #define NVRAM_REG_STAT_B                  0x0b
65 #define NVRAM_REG_STAT_C                  0x0c
66 #define NVRAM_REG_STAT_D                  0x0d
67 #define NVRAM_REG_DIAGNOSTIC_STATUS       0x0e  
68 #define NVRAM_REG_SHUTDOWN_STATUS         0x0f
69
70 #define NVRAM_IBM_HD_DATA                 0x12
71 #define NVRAM_IDE_TRANSLATION             0x39
72
73 #define NVRAM_REG_FLOPPY_TYPE             0x10
74 #define NVRAM_REG_EQUIPMENT_BYTE          0x14
75
76 #define NVRAM_REG_BASE_MEMORY_HIGH        0x16
77 #define NVRAM_REG_BASE_MEMORY_LOW         0x15
78
79 #define NVRAM_REG_EXT_MEMORY_HIGH         0x18
80 #define NVRAM_REG_EXT_MEMORY_LOW          0x17
81
82 #define NVRAM_REG_EXT_MEMORY_2ND_HIGH     0x31
83 #define NVRAM_REG_EXT_MEMORY_2ND_LOW      0x30
84
85 #define NVRAM_REG_BOOTSEQ_OLD             0x2d
86
87 #define NVRAM_REG_AMI_BIG_MEMORY_HIGH     0x35
88 #define NVRAM_REG_AMI_BIG_MEMORY_LOW      0x34
89
90 #define NVRAM_REG_CSUM_HIGH               0x2e
91 #define NVRAM_REG_CSUM_LOW                0x2f
92 #define NVRAM_REG_IBM_CENTURY_BYTE        0x32  
93 #define NVRAM_REG_IBM_PS2_CENTURY_BYTE    0x37  
94
95 #define NVRAM_REG_BOOTSEQ_NEW_FIRST       0x3D
96 #define NVRAM_REG_BOOTSEQ_NEW_SECOND      0x38
97
98 #define CHECKSUM_REGION_FIRST_BYTE        0x10
99 #define CHECKSUM_REGION_LAST_BYTE         0x2d
100
101
102 struct nvram_internal {
103     nvram_state_t dev_state;
104     uint8_t       thereg;
105     uint8_t       mem_state[NVRAM_REG_MAX];
106     uint8_t       reg_map[NVRAM_REG_MAX / 8];
107
108     struct vm_device * ide;
109
110     struct v3_vm_info * vm;
111     
112     struct v3_timer   *timer;
113
114     v3_lock_t nvram_lock;
115
116     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;  // 1=date/time is in bcd, 0=binary
131     uint8_t        rec    : 1;  // 1=rectangular signal
132     uint8_t        ui     : 1;  // 1=update interrupt
133     uint8_t        ai     : 1;  // 1=alarm interrupt
134     uint8_t        pi     : 1;  // 1=periodic interrupt
135     uint8_t        set    : 1;  // 1=blocked update
136 } __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
137
138 struct rtc_statc {
139     uint8_t        res    : 4;  // reserved
140     uint8_t        uf     : 1;  // 1=source of interrupt is update
141     uint8_t        af     : 1;  // 1=source of interrupt is alarm interrupt
142     uint8_t        pf     : 1;  // 1=source of interrupt is periodic interrupt
143     uint8_t        irq    : 1;  // 1=interrupt requested
144 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
145
146 struct rtc_statd {
147     uint8_t        res    : 7;  // reserved
148     uint8_t        val    : 1;  // 1=cmos ram data is OK
149 }  __attribute__((__packed__))  __attribute__((__aligned__ (1))) ;
150
151
152
153
154 struct bcd_num {
155     uint8_t bot : 4;
156     uint8_t top : 4;
157 } __attribute__((packed));;
158
159
160
161 static void set_reg_num(struct nvram_internal * nvram, uint8_t reg_num) {
162     int major = (reg_num / 8);
163     int minor = reg_num % 8;
164
165     nvram->reg_map[major] |= (0x1 << minor);
166 }
167
168 static int is_reg_set(struct nvram_internal * nvram, uint8_t reg_num) {
169     int major = (reg_num / 8);
170     int minor = reg_num % 8;
171     
172     return (nvram->reg_map[major] & (0x1 << minor)) ? 1 : 0;
173 }
174
175
176 static void set_memory(struct nvram_internal * nvram, uint8_t reg, uint8_t val) {
177     set_reg_num(nvram, reg);
178     nvram->mem_state[reg] = val;
179 }
180
181 static int get_memory(struct nvram_internal * nvram, uint8_t reg, uint8_t * val) {
182
183     if (!is_reg_set(nvram, reg)) {
184         *val = 0;
185         return -1;
186     }
187
188     *val = nvram->mem_state[reg];
189     return 0;
190 }
191
192
193 static uint8_t add_to(uint8_t * left, uint8_t * right, uint8_t bcd) {
194     uint8_t temp;
195
196     if (bcd) { 
197         struct bcd_num * bl = (struct bcd_num *)left;
198         struct bcd_num * br = (struct bcd_num *)right;
199         uint8_t carry = 0;
200
201         bl->bot += br->bot;
202         carry = bl->bot / 0xa;
203         bl->bot %= 0xa;
204
205         bl->top += carry + br->top;
206         carry = bl->top / 0xa;
207         bl->top %= 0xa;
208
209         return carry;
210     } else {
211         temp = *left;
212         *left += *right;
213
214         if (*left < temp) { 
215             return 1;
216         } else {
217             return 0;
218         }
219     }
220 }
221
222
223 static uint8_t days_in_month(uint8_t month, uint8_t bcd) {
224     // This completely ignores Julian / Gregorian stuff right now
225
226     if (bcd) { 
227
228         switch (month) 
229             {
230                 case 0x1: //jan
231                 case 0x3: //march
232                 case 0x5: //may
233                 case 0x7: //july
234                 case 0x8: //aug
235                 case 0x10: //oct
236                 case 0x12: //dec
237                     return 0x31;
238                     break;
239                 case 0x4: //april
240                 case 0x6: //june
241                 case 0x9: //sept
242                 case 0x11: //nov
243                     return 0x30;
244                     break;
245                 case 0x2: //feb
246                     return 0x28;
247                     break;
248                 default:
249                     return 0x30;
250             }
251     
252     } else {
253
254         switch (month) 
255             {
256                 case 1: //jan
257                 case 3: //march
258                 case 5: //may
259                 case 7: //july
260                 case 8: //aug
261                 case 10: //oct
262                 case 12: //dec
263                     return 31;
264                     break;
265                 case 4: //april
266                 case 6: //june
267                 case 9: //sept
268                 case 11: //nov
269                     return 30;
270                     break;
271                 case 2: //feb
272                     return 28;
273                     break;
274                 default:
275                     return 30;
276             }
277     }
278 }
279
280
281 static void update_time(struct nvram_internal * data, 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 == 1);
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     period_us = (1000000*cpu_cycles/cpu_freq);
476
477     update_time(nvram_state,period_us);
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     set_memory(nvram, NVRAM_REG_IBM_PS2_CENTURY_BYTE, 0x20);
688
689     set_memory(nvram, NVRAM_REG_DIAGNOSTIC_STATUS, 0x00);
690     
691     nvram->us = 0;
692     nvram->pus = 0;
693
694     set_memory_size(nvram, vm->mem_size);
695     init_harddrives(nvram);
696     
697     /* compute checksum (must follow all assignments here) */
698     checksum = compute_checksum(nvram);
699     set_memory(nvram, NVRAM_REG_CSUM_HIGH, (checksum >> 8) & 0xff);
700     set_memory(nvram, NVRAM_REG_CSUM_LOW, checksum & 0xff);
701
702     
703     
704     nvram->dev_state = NVRAM_READY;
705     nvram->thereg = 0;
706
707     return 0;
708 }
709
710
711
712
713
714
715 static int nvram_write_reg_port(struct guest_info * core, uint16_t port,
716                                 void * src, uint_t length, void * priv_data) {
717
718     struct nvram_internal * data = priv_data;
719     
720     memcpy(&(data->thereg), src, 1);
721     PrintDebug("nvram: Writing To NVRAM reg: 0x%x\n", data->thereg);
722
723     return 1;
724 }
725
726 static int nvram_read_data_port(struct guest_info * core, uint16_t port,
727                                 void * dst, uint_t length, void * priv_data) {
728
729     struct nvram_internal * data = priv_data;
730
731     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
732
733     if (get_memory(data, data->thereg, (uint8_t *)dst) == -1) {
734         PrintError("nvram: Register %d (0x%x) Not set\n", data->thereg, data->thereg);
735
736         v3_unlock_irqrestore(data->nvram_lock, irq_state);
737
738         /* allow guest to query checksummed bytes; warn but read zero rather than fail in this case */
739         if ((data->thereg >= CHECKSUM_REGION_FIRST_BYTE) && (data->thereg <= CHECKSUM_REGION_LAST_BYTE)) {
740             return 1;
741         } else {        
742             return -1;
743         }
744     }
745
746     PrintDebug("nvram: nvram_read_data_port(0x%x)  =  0x%x\n", data->thereg, *(uint8_t *)dst);
747
748     // hack
749     if (data->thereg == NVRAM_REG_STAT_A) { 
750         data->mem_state[data->thereg] ^= 0x80;  // toggle Update in progess
751     }
752
753     v3_unlock_irqrestore(data->nvram_lock, irq_state);
754
755     return 1;
756 }
757
758
759 static int nvram_write_data_port(struct guest_info * core, uint16_t port,
760                                  void * src, uint_t length, void * priv_data) {
761
762     struct nvram_internal * data = priv_data;
763
764     addr_t irq_state = v3_lock_irqsave(data->nvram_lock);
765
766     set_memory(data, data->thereg, *(uint8_t *)src);
767
768     v3_unlock_irqrestore(data->nvram_lock, irq_state);
769
770     PrintDebug("nvram: nvram_write_data_port(0x%x) = 0x%x\n", 
771                data->thereg, data->mem_state[data->thereg]);
772
773     return 1;
774 }
775
776
777
778
779 static int nvram_free(struct nvram_internal * nvram_state) {
780     
781     // unregister host events
782     struct guest_info *info = &(nvram_state->vm->cores[0]);
783
784     if (nvram_state->timer) { 
785         v3_remove_timer(info,nvram_state->timer);
786     }
787
788     V3_Free(nvram_state);
789     return 0;
790 }
791
792
793
794 static struct v3_timer_ops timer_ops = {
795     .update_timer = nvram_update_timer,
796 };
797
798
799 static struct v3_device_ops dev_ops = {  
800     .free = (int (*)(void *))nvram_free,
801 };
802
803
804
805
806
807 static int nvram_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
808     struct nvram_internal * nvram_state = NULL;
809     struct vm_device * ide = v3_find_dev(vm, v3_cfg_val(cfg, "storage"));
810     char * dev_id = v3_cfg_val(cfg, "ID");
811     int ret = 0;
812
813     if (!ide) {
814         PrintError("nvram: Could not find IDE device\n");
815         return -1;
816     }
817
818     PrintDebug("nvram: init_device\n");
819     nvram_state = (struct nvram_internal *)V3_Malloc(sizeof(struct nvram_internal) + 1000);
820
821     PrintDebug("nvram: internal at %p\n", (void *)nvram_state);
822
823     nvram_state->ide = ide;
824     nvram_state->vm = vm;
825
826     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, nvram_state);
827
828     if (dev == NULL) {
829         PrintError("nvram: Could not attach device %s\n", dev_id);
830         V3_Free(nvram_state);
831         return -1;
832     }
833
834     init_nvram_state(vm, nvram_state);
835
836     // hook ports
837     ret |= v3_dev_hook_io(dev, NVRAM_REG_PORT, NULL, &nvram_write_reg_port);
838     ret |= v3_dev_hook_io(dev, NVRAM_DATA_PORT, &nvram_read_data_port, &nvram_write_data_port);
839   
840     if (ret != 0) {
841         PrintError("nvram: Error hooking NVRAM IO ports\n");
842         v3_remove_device(dev);
843         return -1;
844     }
845
846     nvram_state->timer = v3_add_timer(&(vm->cores[0]),&timer_ops,nvram_state);
847
848     if (nvram_state->timer == NULL ) { 
849         v3_remove_device(dev);
850         return -1;
851     }
852
853     return 0;
854 }
855
856 device_register("NVRAM", nvram_init)