blob: 806a278b39f847416e3524d83eedb9332e63b9ef [file] [log] [blame]
David Hendricks6d62d752011-03-07 21:20:22 -08001/* Copyright 2010, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Alternatively, this software may be distributed under the terms of the
31 * GNU General Public License ("GPL") version 2 as published by the Free
32 * Software Foundation.
33 *
34 * This is ported from the flashmap utility: http://flashmap.googlecode.com
35 */
36
Louis Yung-Chieh Lod5914442011-03-14 18:43:27 +080037#include <stdio.h>
David Hendricks6d62d752011-03-07 21:20:22 -080038#include <stdlib.h>
39#include <string.h>
40
41#include "flash.h"
42#include "fmap.h"
43
Louis Yung-Chieh Lod5914442011-03-14 18:43:27 +080044/* invoke crossystem and parse the returned string.
45 * returns 0xFFFFFFFF if failed to get the value. */
46#define CROSSYSTEM_FAIL (0xFFFFFFFF)
47static uint32_t get_crossystem_fmap_base(struct flashchip *flash) {
48 char cmd[] = "crossystem fmap_base";
49 FILE *fp;
50 int n;
51 char buf[16];
52 unsigned long fmap_base;
53 unsigned long from_top;
54
55 if (!(fp = popen(cmd, "r"))) {
56 return CROSSYSTEM_FAIL;
57 }
58 n = fread(buf, 1, sizeof(buf) - 1, fp);
59 fclose(fp);
60 if (n < 0) {
61 return CROSSYSTEM_FAIL;
62 }
63 buf[n] = '\0';
64 if (strlen(buf) == 0) {
65 return CROSSYSTEM_FAIL;
66 }
67
68 /* fmap_base is the absolute address in CPU address space.
69 * The top of BIOS address aligns to the last byte of address space,
70 * 0xFFFFFFFF. So we have to shift it to the address related to
71 * start of BIOS.
72 *
73 * CPU address flash address
74 * space p space
75 * 0xFFFFFFFF +-------+ --- +-------+ 0x400000
76 * | | ^ | | ^
77 * | 4MB | | | | | from_top
78 * | | v | | v
79 * fmap_base--> | -fmap | ------|--fmap-|-- the offset we need.
80 * ^ | | | |
81 * | +-------+-------+-------+ 0x000000
82 * | | |
83 * | | |
84 * | | |
85 * | | |
86 * 0x00000000 +-------+
87 *
88 */
89 fmap_base = (unsigned long)strtoll(buf, (char **) NULL, 0);
90 from_top = 0xFFFFFFFF - fmap_base + 1;
91 if (from_top > flash->total_size * 1024) {
92 /* Invalid fmap_base value for this chip, like EC's flash. */
93 return CROSSYSTEM_FAIL;
94 }
95 return flash->total_size * 1024 - from_top;
96}
97
David Hendricks6d62d752011-03-07 21:20:22 -080098extern int fmap_find(struct flashchip *flash, uint8_t **buf)
99{
100 unsigned long int offset = 0;
101 uint64_t sig, tmp64;
David Hendricks6d62d752011-03-07 21:20:22 -0800102 struct fmap fmap;
David Hendricksb0c11542011-03-09 15:26:06 -0800103 int fmap_size, fmap_found = 0, stride;
David Hendricks6d62d752011-03-07 21:20:22 -0800104
105 memcpy(&sig, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE));
106
Louis Yung-Chieh Lod5914442011-03-14 18:43:27 +0800107 offset = get_crossystem_fmap_base(flash);
108 if (CROSSYSTEM_FAIL != offset) {
109 if (flash->read(flash, (uint8_t *)&tmp64,
110 offset, sizeof(tmp64))) {
111 msg_gdbg("failed to read flash at "
112 "offset 0x%lx\n", offset);
113 return -1;
114 }
115 if (!memcmp(&tmp64, &sig, sizeof(sig))) {
116 fmap_found = 1;
117 }
118 }
119
David Hendricks6d62d752011-03-07 21:20:22 -0800120 /*
David Hendricks6d62d752011-03-07 21:20:22 -0800121 * For efficient operation, we start with the largest stride possible
122 * and then decrease the stride on each iteration. We will check for a
123 * remainder when modding the offset with the previous stride. This
124 * makes it so that each offset is only checked once.
David Hendricksb0c11542011-03-09 15:26:06 -0800125 *
126 * At some point, programmer transaction overhead becomes greater than
127 * simply copying everything into RAM and checking one byte at a time.
128 * At some arbitrary point, we'll stop being clever and use brute
129 * force instead by copying the while ROM into RAM and searching one
130 * byte at a time.
131 *
132 * In practice, the flash map is usually stored in a write-protected
133 * section of flash which is often at the top of ROM where the boot
134 * vector on x86 resides. Because of this, we will search from top
135 * to bottom.
David Hendricks6d62d752011-03-07 21:20:22 -0800136 */
Louis Yung-Chieh Lod5914442011-03-14 18:43:27 +0800137 for (stride = (flash->total_size * 1024) / 2;
138 stride >= 16;
139 stride /= 2) {
140 if (fmap_found)
141 break;
142
David Hendricksb0c11542011-03-09 15:26:06 -0800143 for (offset = flash->total_size * 1024 - stride;
David Hendricks6d62d752011-03-07 21:20:22 -0800144 offset > 0;
David Hendricksb0c11542011-03-09 15:26:06 -0800145 offset -= stride) {
146 if (offset % (stride * 2) == 0)
David Hendricks6d62d752011-03-07 21:20:22 -0800147 continue;
David Hendricks6d62d752011-03-07 21:20:22 -0800148
149 if (flash->read(flash, (uint8_t *)&tmp64,
150 offset, sizeof(tmp64))) {
151 msg_gdbg("failed to read flash at "
152 "offset 0x%lx\n", offset);
153 return -1;
154 }
155
156 if (!memcmp(&tmp64, &sig, sizeof(sig))) {
157 fmap_found = 1;
158 break;
159 }
160 }
David Hendricks6d62d752011-03-07 21:20:22 -0800161 }
162
David Hendricksb0c11542011-03-09 15:26:06 -0800163 /* brute force */
164 /* FIXME: This results in the entire ROM being read twice -- once here
165 * and again in doit(). The performance penalty needs to be dealt
166 * with before going upstream.
167 */
168 if (!fmap_found) {
169 uint8_t *image = malloc(flash->total_size * 1024);
170
171 msg_gdbg("using brute force method to find fmap\n");
172 flash->read(flash, image, 0, flash->total_size * 1024);
173 for (offset = flash->total_size * 1024 - sizeof(sig);
174 offset > 0;
175 offset--) {
176 if (!memcmp(&image[offset], &sig, sizeof(sig))) {
177 fmap_found = 1;
178 break;
179 }
180 }
181 free(image);
182 }
183
David Hendricks6d62d752011-03-07 21:20:22 -0800184 if (!fmap_found)
185 return 0;
186
187 if (flash->read(flash, (uint8_t *)&fmap, offset, sizeof(fmap))) {
188 msg_gdbg("failed to read flash at offset 0x%lx\n", offset);
189 return -1;
190 }
191
192 fmap_size = sizeof(fmap) + (fmap.nareas * sizeof(struct fmap_area));
193 *buf = malloc(fmap_size);
194
195 if (flash->read(flash, *buf, offset, fmap_size)) {
196 msg_gdbg("failed to read %d bytes at offset 0x%lx\n",
197 fmap_size, offset);
198 return -1;
199 }
200 return fmap_size;
201}