Contiki 3.x
elfloader-avr.c
1 /*
2  * Copyright (c) 2005, Swedish Institute of Computer Science
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 #include <stdio.h>
34 #include <avr/boot.h>
35 #include <avr/pgmspace.h>
36 #include <avr/interrupt.h>
37 #include "elfloader-arch.h"
38 #include "lib/mmem.h"
39 #include <string.h> //memset
40 
41 #define R_AVR_NONE 0
42 #define R_AVR_32 1
43 #define R_AVR_7_PCREL 2
44 #define R_AVR_13_PCREL 3
45 #define R_AVR_16 4
46 #define R_AVR_16_PM 5
47 #define R_AVR_LO8_LDI 6
48 #define R_AVR_HI8_LDI 7
49 #define R_AVR_HH8_LDI 8
50 #define R_AVR_LO8_LDI_NEG 9
51 #define R_AVR_HI8_LDI_NEG 10
52 #define R_AVR_HH8_LDI_NEG 11
53 #define R_AVR_LO8_LDI_PM 12
54 #define R_AVR_HI8_LDI_PM 13
55 #define R_AVR_HH8_LDI_PM 14
56 #define R_AVR_LO8_LDI_PM_NEG 15
57 #define R_AVR_HI8_LDI_PM_NEG 16
58 #define R_AVR_HH8_LDI_PM_NEG 17
59 #define R_AVR_CALL 18
60 
61 #define ELF32_R_TYPE(info) ((unsigned char)(info))
62 
63 #define DEBUG 0
64 #if DEBUG
65 #include <avr/pgmspace.h>
66 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
67 #else
68 #define PRINTF(...)
69 #endif
70 
71 static struct mmem module_heap;
72 /*---------------------------------------------------------------------------*/
73 void*
75 {
76  /* Free previously allocated memory */
77  /* TODO Assumes memory address 0 can't be allocated, use flag instead? */
78  if (MMEM_PTR(&module_heap) != 0) {
79  mmem_free(&module_heap);
80  }
81 
82  /* Allocate RAM for module */
83  if (mmem_alloc (&module_heap, size) == 0) {
84  return NULL;
85  }
86 
87  return (char*)MMEM_PTR(&module_heap);
88 }
89 
90 /*---------------------------------------------------------------------------*/
91 /* TODO: Currently, modules are written to the fixed address 0x10000. Since
92  * flash rom uses word addresses on the AVR, we return 0x8000 here
93  */
94 void*
96 {
97  return (void *)0x8000;
98 }
99 
100 /*---------------------------------------------------------------------------*/
101 /* Eliminate compiler warnings for (non-functional) code when flash requires 32 bit addresses and pointers are 16 bit */
102 #define INCLUDE_APPLICATE_SOURCE 1
103 #ifdef __GNUC__
104 #if (FLASHEND > USHRT_MAX) && (__SIZEOF_POINTER__ <= 2)
105 #undef INCLUDE_APPLICATE_SOURCE
106 #define INCLUDE_APPLICATE_SOURCE 0
107 #endif
108 #if (__SIZEOF_POINTER__ > 2)
109 #define INCLUDE_32BIT_CODE 1
110 #endif
111 #endif
112 #if INCLUDE_APPLICATE_SOURCE
113 
114 BOOTLOADER_SECTION void
115 elfloader_arch_write_rom(int fd, unsigned short textoff, unsigned int size, char *mem)
116 {
117  unsigned char buf[SPM_PAGESIZE];
118  unsigned short* flashptr = (unsigned short *) mem;
119 
120 
121  // Sanity-check size of loadable module
122  if (size <= 0)
123  return;
124 
125 
126  // Seek to patched module and burn it to flash (in chunks of
127  // size SPM_PAGESIZE, i.e. 256 bytes on the ATmega128)
128  cfs_seek(fd, textoff, CFS_SEEK_SET);
129  for (flashptr=(unsigned short *)mem; flashptr < (unsigned short *) mem + size; flashptr += SPM_PAGESIZE) {
130  memset (buf, 0, SPM_PAGESIZE);
131  cfs_read(fd, buf, SPM_PAGESIZE);
132 
133  // Disable interrupts
134  uint8_t sreg;
135  sreg = SREG;
136  cli ();
137 
138  // Erase flash page
139  boot_page_erase (flashptr);
140  boot_spm_busy_wait ();
141 
142  unsigned short *origptr = flashptr;
143 
144  int i;
145  // Store data into page buffer
146  for(i = 0; i < SPM_PAGESIZE; i+=2) {
147  boot_page_fill (flashptr, (uint16_t)((buf[i+1] << 8) | buf[i]));
148  PORTB = 0xff - 7;
149  ++flashptr;
150  }
151 
152  // Burn page
153  boot_page_write (origptr);
154  boot_spm_busy_wait();
155 
156  // Reenable RWW sectin
157  boot_rww_enable ();
158  boot_spm_busy_wait ();
159 
160  // Restore original interrupt settings
161  SREG = sreg;
162  }
163 }
164 #endif /* INCLUDE_APPLICATE_SOURCE */
165 
166 /*---------------------------------------------------------------------------*/
167 static void
168 write_ldi(int fd, unsigned char *instr, unsigned char byte)
169 {
170  instr[0] = (instr[0] & 0xf0) | (byte & 0x0f);
171  instr[1] = (instr[1] & 0xf0) | (byte >> 4);
172  cfs_write (fd, instr, 2);
173 }
174 /*---------------------------------------------------------------------------*/
175 void
176 elfloader_arch_relocate(int fd, unsigned int sectionoffset,
177  // struct elf32_rela *rela, elf32_addr addr)
178  char *sectionaddr,
179  struct elf32_rela *rela, char *addr)
180 {
181  unsigned int type;
182  unsigned char instr[4];
183 
184  cfs_seek(fd, sectionoffset + rela->r_offset, CFS_SEEK_SET);
185  cfs_read(fd, instr, 4);
186  cfs_seek(fd, sectionoffset + rela->r_offset, CFS_SEEK_SET);
187 
188  type = ELF32_R_TYPE(rela->r_info);
189 
190  addr += rela->r_addend;
191 
192  switch(type) {
193  case R_AVR_NONE:
194  case R_AVR_32:
195  PRINTF(PSTR ("elfloader-avr.c: unsupported relocation type: "));
196  PRINTF("%d\n", type);
197  break;
198 
199  case R_AVR_7_PCREL: { /* 4 */
200  /*
201  * Relocation is relative to PC. -2: branch instructions add 2 to PC.
202  * Do not use >> 1 for division because branch instructions use
203  * signed offsets.
204  */
205  int16_t a = (((int)addr - rela->r_offset -2) / 2);
206  instr[0] |= (a << 3) & 0xf8;
207  instr[1] |= (a >> 5) & 0x03;
208  cfs_write(fd, instr, 2);
209  }
210  break;
211  case R_AVR_13_PCREL: { /* 3 */
212  /*
213  * Relocation is relative to PC. -2: RJMP adds 2 to PC.
214  * Do not use >> 1 for division because RJMP uses signed offsets.
215  */
216  int16_t a = (int)addr / 2;
217  a -= rela->r_offset / 2;
218  a--;
219  instr[0] |= a & 0xff;
220  instr[1] |= (a >> 8) & 0x0f;
221  cfs_write(fd, instr, 2);
222  }
223  break;
224 
225  case R_AVR_16: /* 4 */
226  instr[0] = (int)addr & 0xff;
227  instr[1] = ((int)addr >> 8) & 0xff;
228 
229  cfs_write(fd, instr, 2);
230  break;
231 
232  case R_AVR_16_PM: /* 5 */
233  addr = (char *)((int)addr >> 1);
234  instr[0] = (int)addr & 0xff;
235  instr[1] = ((int)addr >> 8) & 0xff;
236 
237  cfs_write(fd, instr, 2);
238  break;
239 
240  case R_AVR_LO8_LDI: /* 6 */
241  write_ldi(fd, instr, (int)addr);
242  break;
243  case R_AVR_HI8_LDI: /* 7 */
244  write_ldi(fd, instr, (int)addr >> 8);
245  break;
246 
247 #if INCLUDE_32BIT_CODE /* 32 bit AVRs */
248  case R_AVR_HH8_LDI: /* 8 */
249  write_ldi(fd, instr, (int)addr >> 16);
250  break;
251 #endif
252 
253  case R_AVR_LO8_LDI_NEG: /* 9 */
254  addr = (char *) (0 - (int)addr);
255  write_ldi(fd, instr, (int)addr);
256  break;
257  case R_AVR_HI8_LDI_NEG: /* 10 */
258  addr = (char *) (0 - (int)addr);
259  write_ldi(fd, instr, (int)addr >> 8);
260  break;
261 
262 #if INCLUDE_32BIT_CODE /* 32 bit AVRs */
263  case R_AVR_HH8_LDI_NEG: /* 11 */
264  addr = (char *)(0 - (int)addr);
265  write_ldi(fd, instr, (int)addr >> 16);
266  break;
267 #endif
268 
269  case R_AVR_LO8_LDI_PM: /* 12 */
270  write_ldi(fd, instr, (int)addr >> 1);
271  break;
272  case R_AVR_HI8_LDI_PM: /* 13 */
273  write_ldi(fd, instr, (int)addr >> 9);
274  break;
275 
276 #if INCLUDE_32BIT_CODE /* 32 bit AVRs */
277  case R_AVR_HH8_LDI_PM: /* 14 */
278  write_ldi(fd, instr, (int)addr >> 17);
279  break;
280 #endif
281 
282  case R_AVR_LO8_LDI_PM_NEG: /* 15 */
283  addr = (char *) (0 - (int)addr);
284  write_ldi(fd, instr, (int)addr >> 1);
285  break;
286  case R_AVR_HI8_LDI_PM_NEG: /* 16 */
287  addr = (char *) (0 - (int)addr);
288  write_ldi(fd, instr, (int)addr >> 9);
289  break;
290 
291 #if INCLUDE_32BIT_CODE /* 32 bit AVRs */
292  case R_AVR_HH8_LDI_PM_NEG: /* 17 */
293  addr = (char *) (0 - (int)addr);
294  write_ldi(fd, instr, (int)addr >> 17);
295  break;
296 #endif
297 
298  case R_AVR_CALL: /* 18 */
299  /* old solution:
300  addr = ((int16_t)addr >> 1);
301  instr[2] = (int16_t)addr & 0xff;
302  instr[3] = (int16_t)addr >> 8;
303  */
304 
305  /* new solution */
306  instr[2] = (uint8_t) ((int)addr) & 0xff;
307  instr[3] = ((int)addr) >> 8;
308  cfs_write(fd, instr, 4);
309  break;
310 
311  default:
312  PRINTF(PSTR ("Unknown relocation type!\n"));
313  break;
314  }
315 }
316 /*---------------------------------------------------------------------------*/
317 void
318 elfloader_unload(void) {
319 }
void elfloader_arch_relocate(int fd, unsigned int sectionoffset, char *sectionaddr, struct elf32_rela *rela, char *addr)
Perform a relocation.
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)
Seek to a specified position in an open file.
Definition: cfs-coffee.c:1042
Header file for the managed memory allocator
Header file for the architecture specific parts of the Contiki ELF loader.
#define PORTB
Peripheral PORTB base pointer.
Definition: MK60D10.h:6419
#define NULL
The null pointer.
void * elfloader_arch_allocate_ram(int size)
Allocate RAM for a new module.
Definition: elfloader-avr.c:74
#define MMEM_PTR(m)
Get a pointer to the managed memory.
Definition: mmem.h:75
int mmem_alloc(struct mmem *m, unsigned int size)
Allocate a managed memory block.
Definition: mmem.c:83
void mmem_free(struct mmem *m)
Deallocate a managed memory block.
Definition: mmem.c:119
void * elfloader_arch_allocate_rom(int size)
Allocate program memory for a new module.
Definition: elfloader-avr.c:95
#define CFS_SEEK_SET
Specify that cfs_seek() should compute the offset from the beginning of the file. ...
Definition: cfs.h:127
void elfloader_arch_write_rom(int fd, unsigned short textoff, unsigned int size, char *mem)
Write to read-only memory (for example the text segment).