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.


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