PowerPC64 ld segfault with code in non-executable sections
[external/binutils.git] / sim / ppc / hw_nvram.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, see <http://www.gnu.org/licenses/>.
17  
18     */
19
20
21 #ifndef _HW_NVRAM_C_
22 #define _HW_NVRAM_C_
23
24 #ifndef STATIC_INLINE_HW_NVRAM
25 #define STATIC_INLINE_HW_NVRAM STATIC_INLINE
26 #endif
27
28 #include "device_table.h"
29
30 #ifdef HAVE_TIME_H
31 #include <time.h>
32 #endif
33
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #else
37 #ifdef HAVE_STRINGS_H
38 #include <strings.h>
39 #endif
40 #endif
41
42 /* DEVICE
43
44
45    nvram - non-volatile memory with clock
46
47
48    DESCRIPTION
49
50
51    This device implements a small byte addressable non-volatile
52    memory.  The top 8 bytes of this memory include a real-time clock.
53
54
55    PROPERTIES
56
57
58    reg = <address> <size> (required)
59
60    Specify the address/size of this device within its parents address
61    space.
62
63
64    timezone = <integer> (optional)
65
66    Adjustment to the hosts current GMT (in seconds) that should be
67    applied when updating the NVRAM's clock.  If no timezone is
68    specified, zero (GMT or UCT) is assumed.
69
70
71    */
72
73 typedef struct _hw_nvram_device {
74   unsigned8 *memory;
75   unsigned sizeof_memory;
76 #ifdef HAVE_TIME_H
77   time_t host_time;
78 #else
79   long host_time;
80 #endif
81   unsigned timezone;
82   /* useful */
83   unsigned addr_year;
84   unsigned addr_month;
85   unsigned addr_date;
86   unsigned addr_day;
87   unsigned addr_hour;
88   unsigned addr_minutes;
89   unsigned addr_seconds;
90   unsigned addr_control;
91 } hw_nvram_device;
92
93 static void *
94 hw_nvram_create(const char *name,
95                 const device_unit *unit_address,
96                 const char *args)
97 {
98   hw_nvram_device *nvram = ZALLOC(hw_nvram_device);
99   return nvram;
100 }
101
102 typedef struct _hw_nvram_reg_spec {
103   unsigned32 base;
104   unsigned32 size;
105 } hw_nvram_reg_spec;
106
107 static void
108 hw_nvram_init_address(device *me)
109 {
110   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
111   
112   /* use the generic init code to attach this device to its parent bus */
113   generic_device_init_address(me);
114
115   /* find the first non zero reg property and use that as the device
116      size */
117   if (nvram->sizeof_memory == 0) {
118     reg_property_spec reg;
119     int reg_nr;
120     for (reg_nr = 0;
121          device_find_reg_array_property(me, "reg", reg_nr, &reg);
122          reg_nr++) {
123       unsigned attach_size;
124       if (device_size_to_attach_size(device_parent(me),
125                                      &reg.size, &attach_size,
126                                      me)) {
127         nvram->sizeof_memory = attach_size;
128         break;
129       }
130     }
131     if (nvram->sizeof_memory == 0)
132       device_error(me, "reg property must contain a non-zero phys-addr:size tupple");
133     if (nvram->sizeof_memory < 8)
134       device_error(me, "NVRAM must be at least 8 bytes in size");
135   }
136
137   /* initialize the hw_nvram */
138   if (nvram->memory == NULL) {
139     nvram->memory = zalloc(nvram->sizeof_memory);
140   }
141   else
142     memset(nvram->memory, 0, nvram->sizeof_memory);
143   
144   if (device_find_property(me, "timezone") == NULL)
145     nvram->timezone = 0;
146   else
147     nvram->timezone = device_find_integer_property(me, "timezone");
148   
149   nvram->addr_year = nvram->sizeof_memory - 1;
150   nvram->addr_month = nvram->sizeof_memory - 2;
151   nvram->addr_date = nvram->sizeof_memory - 3;
152   nvram->addr_day = nvram->sizeof_memory - 4;
153   nvram->addr_hour = nvram->sizeof_memory - 5;
154   nvram->addr_minutes = nvram->sizeof_memory - 6;
155   nvram->addr_seconds = nvram->sizeof_memory - 7;
156   nvram->addr_control = nvram->sizeof_memory - 8;
157   
158 }
159
160 static int
161 hw_nvram_bcd(int val)
162 {
163   val = val % 100;
164   if (val < 0)
165     val += 100;
166   return ((val / 10) << 4) + (val % 10);
167 }
168
169
170 /* If reached an update interval and allowed, update the clock within
171    the hw_nvram.  While this function could be implemented using events
172    it isn't on the assumption that the HW_NVRAM will hardly ever be
173    referenced and hence there is little need in keeping the clock
174    continually up-to-date */
175
176 static void
177 hw_nvram_update_clock(hw_nvram_device *nvram,
178                       cpu *processor)
179 {
180 #ifdef HAVE_TIME_H
181   if (!(nvram->memory[nvram->addr_control] & 0xc0)) {
182     time_t host_time = time(NULL);
183     if (nvram->host_time != host_time) {
184       time_t nvtime = host_time + nvram->timezone;
185       struct tm *clock = gmtime(&nvtime);
186       nvram->host_time = host_time;
187       nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year);
188       nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1);
189       nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday);
190       nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1);
191       nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour);
192       nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min);
193       nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec);
194     }
195   }
196 #else
197   error("fixme - where do I find out GMT\n");
198 #endif
199 }
200
201 static void
202 hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor)
203 {
204   error ("fixme - how do I set the localtime\n");
205 }
206
207 static unsigned
208 hw_nvram_io_read_buffer(device *me,
209                         void *dest,
210                         int space,
211                         unsigned_word addr,
212                         unsigned nr_bytes,
213                         cpu *processor,
214                         unsigned_word cia)
215 {
216   int i;
217   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
218   for (i = 0; i < nr_bytes; i++) {
219     unsigned address = (addr + i) % nvram->sizeof_memory;
220     unsigned8 data = nvram->memory[address];
221     hw_nvram_update_clock(nvram, processor);
222     ((unsigned8*)dest)[i] = data;
223   }
224   return nr_bytes;
225 }
226
227 static unsigned
228 hw_nvram_io_write_buffer(device *me,
229                          const void *source,
230                          int space,
231                          unsigned_word addr,
232                          unsigned nr_bytes,
233                          cpu *processor,
234                          unsigned_word cia)
235 {
236   int i;
237   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
238   for (i = 0; i < nr_bytes; i++) {
239     unsigned address = (addr + i) % nvram->sizeof_memory;
240     unsigned8 data = ((unsigned8*)source)[i];
241     if (address == nvram->addr_control
242         && (data & 0x80) == 0
243         && (nvram->memory[address] & 0x80) == 0x80)
244       hw_nvram_set_clock(nvram, processor);
245     else
246       hw_nvram_update_clock(nvram, processor);
247     nvram->memory[address] = data;
248   }
249   return nr_bytes;
250 }
251
252 static device_callbacks const hw_nvram_callbacks = {
253   { hw_nvram_init_address, },
254   { NULL, }, /* address */
255   { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */
256 };
257
258 const device_descriptor hw_nvram_device_descriptor[] = {
259   { "nvram", hw_nvram_create, &hw_nvram_callbacks },
260   { NULL },
261 };
262
263 #endif /* _HW_NVRAM_C_ */