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 <stdint.h>
24 #include <xen/libelf/elfstructs.h>
25 
26 #include "util.h"
27 #include "config.h"
28 
29 #include "../rombios/32bit/32bitbios_flat.h"
30 
relocate_32bitbios(char * elfarray,uint32_t elfarraysize)31 static uint32_t relocate_32bitbios(char *elfarray, uint32_t elfarraysize)
32 {
33     Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfarray;
34     Elf32_Shdr *shdr = (Elf32_Shdr *)&elfarray[ehdr->e_shoff];
35     uint32_t reloc_off, reloc_size;
36     char *highbiosarea;
37     int i;
38 
39     /*
40      * Step 1. General elf cleanup, and compute total relocation size.
41      */
42     reloc_off = 0;
43     for ( i = 0; i < ehdr->e_shnum; i++ )
44     {
45         /* By default all section data points into elf image data array. */
46         shdr[i].sh_addr = (Elf32_Addr)&elfarray[shdr[i].sh_offset];
47 
48         /* Fix up a corner case of address alignment. */
49         if ( shdr[i].sh_addralign == 0 )
50             shdr[i].sh_addralign = 1;
51 
52         /* Any section which contains run-time data must be relocated. */
53         if ( shdr[i].sh_flags & SHF_ALLOC )
54         {
55             uint32_t mask = shdr[i].sh_addralign - 1;
56             reloc_off = (reloc_off + mask) & ~mask;
57             reloc_off += shdr[i].sh_size;
58         }
59     }
60 
61     /*
62      * Step 2. Now we know the relocation size, allocate a chunk of high mem.
63      */
64     reloc_size = reloc_off;
65     printf("%d bytes of ROMBIOS high-memory extensions:\n", reloc_size);
66     highbiosarea = mem_alloc(reloc_size, 1024);
67     BUG_ON(highbiosarea == NULL);
68     printf("  Relocating to 0x%x-0x%x ... ",
69            (uint32_t)&highbiosarea[0],
70            (uint32_t)&highbiosarea[reloc_size]);
71 
72     /*
73      * Step 3. Copy run-time data into the newly-allocated high-memory chunk.
74      */
75     reloc_off = 0;
76     for ( i = 0; i < ehdr->e_shnum; i++ )
77     {
78         uint32_t mask = shdr[i].sh_addralign - 1;
79 
80         /* Nothing to do for non-run-time sections. */
81         if ( !(shdr[i].sh_flags & SHF_ALLOC) )
82             continue;
83 
84         /* Copy from old location. */
85         reloc_off = (reloc_off + mask) & ~mask;
86         if ( shdr[i].sh_type == SHT_NOBITS )
87             memset(&highbiosarea[reloc_off], 0, shdr[i].sh_size);
88         else
89             memcpy(&highbiosarea[reloc_off], (void *)shdr[i].sh_addr,
90                    shdr[i].sh_size);
91 
92         /* Update address to new location. */
93         shdr[i].sh_addr = (Elf32_Addr)&highbiosarea[reloc_off];
94         reloc_off += shdr[i].sh_size;
95     }
96     BUG_ON(reloc_off != reloc_size);
97 
98     /*
99      * Step 4. Perform relocations in high memory.
100      */
101     for ( i = 0; i < ehdr->e_shnum; i++ )
102     {
103         Elf32_Sym  *syms, *sym;
104         Elf32_Rel  *rels;
105         char       *code;
106         uint32_t   *loc, fix;
107         int         j;
108 
109         if ( shdr[i].sh_type == SHT_RELA )
110             printf("Unsupported section type SHT_RELA\n");
111 
112         if ( shdr[i].sh_type != SHT_REL )
113             continue;
114 
115         syms = (Elf32_Sym *)shdr[shdr[i].sh_link].sh_addr;
116         rels = (Elf32_Rel *)shdr[i].sh_addr;
117         code = (char      *)shdr[shdr[i].sh_info].sh_addr;
118 
119         for ( j = 0; j < shdr[i].sh_size / sizeof(Elf32_Rel); j++ )
120         {
121             sym = &syms[ELF32_R_SYM(rels[j].r_info)];
122             loc = (uint32_t *)&code[rels[j].r_offset];
123             fix = shdr[sym->st_shndx].sh_addr + sym->st_value;
124 
125             switch ( ELF32_R_TYPE(rels[j].r_info) )
126             {
127             case R_386_PC32:
128                 *loc += fix - (uint32_t)loc;
129                 break;
130 
131             case R_386_32:
132                 *loc += fix;
133                 break;
134             }
135         }
136     }
137 
138     printf("done\n");
139 
140     return (uint32_t)highbiosarea;
141 }
142 
rombios_highbios_setup(void)143 uint32_t rombios_highbios_setup(void)
144 {
145     return relocate_32bitbios((char *)highbios_array, sizeof(highbios_array));
146 }
147 
148 /*
149  * Local variables:
150  * mode: C
151  * c-file-style: "BSD"
152  * c-basic-offset: 4
153  * tab-width: 4
154  * indent-tabs-mode: nil
155  * End:
156  */
157