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.


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