1 /*
2  * 32bitbios_support.c - relocation of 32bit BIOS implementation
3  *
4  * Stefan Berger, stefanb@us.ibm.com
5  * Copyright (c) 2006, International Business Machines Corporation.
6  *
7  * Keir Fraser, keir@xensource.com
8  * Copyright (c) 2007, XenSource Inc.
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms and conditions of the GNU General Public License,
12  * version 2, as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <inttypes.h>
24 #include <elf.h>
25 #ifdef __sun__
26 #include <sys/machelf.h>
27 #endif
28 
29 #include "util.h"
30 #include "config.h"
31 
32 #include "../rombios/32bit/32bitbios_flat.h"
33 
relocate_32bitbios(char * elfarray,uint32_t elfarraysize)34 static uint32_t relocate_32bitbios(char *elfarray, uint32_t elfarraysize)
35 {
36     Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfarray;
37     Elf32_Shdr *shdr = (Elf32_Shdr *)&elfarray[ehdr->e_shoff];
38     uint32_t reloc_off, reloc_size;
39     char *highbiosarea;
40     int i;
41 
42     /*
43      * Step 1. General elf cleanup, and compute total relocation size.
44      */
45     reloc_off = 0;
46     for ( i = 0; i < ehdr->e_shnum; i++ )
47     {
48         /* By default all section data points into elf image data array. */
49         shdr[i].sh_addr = (Elf32_Addr)&elfarray[shdr[i].sh_offset];
50 
51         /* Fix up a corner case of address alignment. */
52         if ( shdr[i].sh_addralign == 0 )
53             shdr[i].sh_addralign = 1;
54 
55         /* Any section which contains run-time data must be relocated. */
56         if ( shdr[i].sh_flags & SHF_ALLOC )
57         {
58             uint32_t mask = shdr[i].sh_addralign - 1;
59             reloc_off = (reloc_off + mask) & ~mask;
60             reloc_off += shdr[i].sh_size;
61         }
62     }
63 
64     /*
65      * Step 2. Now we know the relocation size, allocate a chunk of high mem.
66      */
67     reloc_size = reloc_off;
68     printf("%d bytes of ROMBIOS high-memory extensions:\n", reloc_size);
69     highbiosarea = mem_alloc(reloc_size, 1024);
70     BUG_ON(highbiosarea == NULL);
71     printf("  Relocating to 0x%x-0x%x ... ",
72            (uint32_t)&highbiosarea[0],
73            (uint32_t)&highbiosarea[reloc_size]);
74 
75     /*
76      * Step 3. Copy run-time data into the newly-allocated high-memory chunk.
77      */
78     reloc_off = 0;
79     for ( i = 0; i < ehdr->e_shnum; i++ )
80     {
81         uint32_t mask = shdr[i].sh_addralign - 1;
82 
83         /* Nothing to do for non-run-time sections. */
84         if ( !(shdr[i].sh_flags & SHF_ALLOC) )
85             continue;
86 
87         /* Copy from old location. */
88         reloc_off = (reloc_off + mask) & ~mask;
89         if ( shdr[i].sh_type == SHT_NOBITS )
90             memset(&highbiosarea[reloc_off], 0, shdr[i].sh_size);
91         else
92             memcpy(&highbiosarea[reloc_off], (void *)shdr[i].sh_addr,
93                    shdr[i].sh_size);
94 
95         /* Update address to new location. */
96         shdr[i].sh_addr = (Elf32_Addr)&highbiosarea[reloc_off];
97         reloc_off += shdr[i].sh_size;
98     }
99     BUG_ON(reloc_off != reloc_size);
100 
101     /*
102      * Step 4. Perform relocations in high memory.
103      */
104     for ( i = 0; i < ehdr->e_shnum; i++ )
105     {
106         Elf32_Sym  *syms, *sym;
107         Elf32_Rel  *rels;
108         char       *code;
109         uint32_t   *loc, fix;
110         int         j;
111 
112         if ( shdr[i].sh_type == SHT_RELA )
113             printf("Unsupported section type SHT_RELA\n");
114 
115         if ( shdr[i].sh_type != SHT_REL )
116             continue;
117 
118         syms = (Elf32_Sym *)shdr[shdr[i].sh_link].sh_addr;
119         rels = (Elf32_Rel *)shdr[i].sh_addr;
120         code = (char      *)shdr[shdr[i].sh_info].sh_addr;
121 
122         for ( j = 0; j < shdr[i].sh_size / sizeof(Elf32_Rel); j++ )
123         {
124             sym = &syms[ELF32_R_SYM(rels[j].r_info)];
125             loc = (uint32_t *)&code[rels[j].r_offset];
126             fix = shdr[sym->st_shndx].sh_addr + sym->st_value;
127 
128             switch ( ELF32_R_TYPE(rels[j].r_info) )
129             {
130             case R_386_PC32:
131                 *loc += fix - (uint32_t)loc;
132                 break;
133 
134             case R_386_32:
135                 *loc += fix;
136                 break;
137             }
138         }
139     }
140 
141     printf("done\n");
142 
143     return (uint32_t)highbiosarea;
144 }
145 
rombios_highbios_setup(void)146 uint32_t rombios_highbios_setup(void)
147 {
148     return relocate_32bitbios((char *)highbios_array, sizeof(highbios_array));
149 }
150 
151 /*
152  * Local variables:
153  * mode: C
154  * c-file-style: "BSD"
155  * c-basic-offset: 4
156  * tab-width: 4
157  * indent-tabs-mode: nil
158  * End:
159  */
160