Root/
Source at commit 429 created 13 years 9 months ago. By meklort, Updated module system. Hooks can now be used within modules when cetaion functions are called in chameleon. Note that onle two hooks currently exist, more need to be added. I also updated the HelloWorld module to use a hook instead of print out right away. | |
---|---|
1 | /*␊ |
2 | * spd.c - serial presence detect memory information␊ |
3 | * (restored from pcefi10.5)␊ |
4 | */␊ |
5 | ␊ |
6 | #include "libsaio.h"␊ |
7 | #include "pci.h"␊ |
8 | #include "platform.h"␊ |
9 | #include "spd.h"␊ |
10 | #include "saio_internal.h"␊ |
11 | #include "bootstruct.h"␊ |
12 | #include "memvendors.h"␊ |
13 | ␊ |
14 | #ifndef DEBUG_SPD␊ |
15 | #define DEBUG_SPD 0␊ |
16 | #endif␊ |
17 | ␊ |
18 | #if DEBUG_SPD␊ |
19 | #define DBG(x...)␉printf(x)␊ |
20 | #else␊ |
21 | #define DBG(x...)␊ |
22 | #endif␊ |
23 | ␊ |
24 | static const char *spd_memory_types[] =␊ |
25 | {␊ |
26 | ␉"RAM", /* 00h Undefined */␊ |
27 | ␉"FPM", /* 01h FPM */␊ |
28 | ␉"EDO", /* 02h EDO */␊ |
29 | ␉"",␉␉␉␉/* 03h PIPELINE NIBBLE */␊ |
30 | ␉"SDRAM", /* 04h SDRAM */␊ |
31 | ␉"",␉␉␉␉/* 05h MULTIPLEXED ROM */␊ |
32 | ␉"DDR SGRAM",␉/* 06h SGRAM DDR */␊ |
33 | ␉"DDR SDRAM",␉/* 07h SDRAM DDR */␊ |
34 | ␉"DDR2 SDRAM", /* 08h SDRAM DDR 2 */␊ |
35 | ␉"",␉␉␉␉/* 09h Undefined */␊ |
36 | ␉"",␉␉␉␉/* 0Ah Undefined */␊ |
37 | ␉"DDR3 SDRAM" /* 0Bh SDRAM DDR 3 */␊ |
38 | };␊ |
39 | ␊ |
40 | #define UNKNOWN_MEM_TYPE 2␊ |
41 | static uint8_t spd_mem_to_smbios[] =␊ |
42 | {␊ |
43 | ␉UNKNOWN_MEM_TYPE, /* 00h Undefined */␊ |
44 | ␉UNKNOWN_MEM_TYPE, /* 01h FPM */␊ |
45 | ␉UNKNOWN_MEM_TYPE, /* 02h EDO */␊ |
46 | ␉UNKNOWN_MEM_TYPE,␉ /* 03h PIPELINE NIBBLE */␊ |
47 | ␉SMB_MEM_TYPE_SDRAM, /* 04h SDRAM */␊ |
48 | ␉SMB_MEM_TYPE_ROM,␉ /* 05h MULTIPLEXED ROM */␊ |
49 | ␉SMB_MEM_TYPE_SGRAM, /* 06h SGRAM DDR */␊ |
50 | ␉SMB_MEM_TYPE_DDR, /* 07h SDRAM DDR */␊ |
51 | ␉SMB_MEM_TYPE_DDR2, /* 08h SDRAM DDR 2 */␊ |
52 | ␉UNKNOWN_MEM_TYPE, ␉ /* 09h Undefined */␊ |
53 | ␉UNKNOWN_MEM_TYPE,␉ /* 0Ah Undefined */␊ |
54 | ␉SMB_MEM_TYPE_DDR3 /* 0Bh SDRAM DDR 3 */␊ |
55 | };␊ |
56 | #define SPD_TO_SMBIOS_SIZE (sizeof(spd_mem_to_smbios)/sizeof(uint8_t))␊ |
57 | ␊ |
58 | #define rdtsc(low,high) \␊ |
59 | __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))␊ |
60 | ␊ |
61 | #define SMBHSTSTS 0␊ |
62 | #define SMBHSTCNT 2␊ |
63 | #define SMBHSTCMD 3␊ |
64 | #define SMBHSTADD 4␊ |
65 | #define SMBHSTDAT 5␊ |
66 | #define SBMBLKDAT 7␊ |
67 | ␊ |
68 | /** Read one byte from the intel i2c, used for reading SPD on intel chipsets only. */␊ |
69 | unsigned char smb_read_byte_intel(uint32_t base, uint8_t adr, uint8_t cmd)␊ |
70 | {␊ |
71 | int l1, h1, l2, h2;␊ |
72 | unsigned long long t;␊ |
73 | ␉␊ |
74 | outb(base + SMBHSTSTS, 0x1f);␉␉␉␉␉// reset SMBus Controller␊ |
75 | outb(base + SMBHSTDAT, 0xff);␊ |
76 | ␉␊ |
77 | while( inb(base + SMBHSTSTS) & 0x01);␉␉␉// wait until ready␊ |
78 | ␉␊ |
79 | outb(base + SMBHSTCMD, cmd);␊ |
80 | outb(base + SMBHSTADD, (adr << 1) | 0x01 );␊ |
81 | outb(base + SMBHSTCNT, 0x48 );␊ |
82 | ␉␊ |
83 | rdtsc(l1, h1);␊ |
84 | ␉␊ |
85 | ␉while (!( inb(base + SMBHSTSTS) & 0x02))␉␉// wait til command finished␊ |
86 | ␉{␉␊ |
87 | ␉␉rdtsc(l2, h2);␊ |
88 | ␉␉t = ((h2 - h1) * 0xffffffff + (l2 - l1)) / (Platform.CPU.TSCFrequency / 40);␊ |
89 | ␉␉if (t > 10)␊ |
90 | ␉␉␉break;␉␉␉␉␉␉␉␉␉// break after 10ms␊ |
91 | }␊ |
92 | return inb(base + SMBHSTDAT);␊ |
93 | }␊ |
94 | ␊ |
95 | /** Get Vendor Name from spd, 2 cases handled DDR3 and DDR2, ␊ |
96 | have different formats, always return a valid ptr.*/␊ |
97 | const char * getVendorName(RamSlotInfo_t* slot)␊ |
98 | {␊ |
99 | uint8_t bank = 0;␊ |
100 | uint8_t code = 0;␊ |
101 | int i = 0;␊ |
102 | const char * spd = slot->spd;␊ |
103 | ␊ |
104 | if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3) { // DDR3␊ |
105 | bank = spd[0x75];␊ |
106 | code = spd[0x76];␊ |
107 | for (i=0; i < VEN_MAP_SIZE; i++)␊ |
108 | if (bank==vendorMap[i].bank && code==vendorMap[i].code)␊ |
109 | return vendorMap[i].name;␊ |
110 | }␊ |
111 | else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2) {␊ |
112 | if(spd[0x40]==0x7f) {␊ |
113 | for (i=0x40; i<0x48 && spd[i]==0x7f;i++) bank++;␊ |
114 | code = spd[i];␊ |
115 | } else {␊ |
116 | code = spd[0x40]; ␊ |
117 | bank = 0;␊ |
118 | }␊ |
119 | for (i=0; i < VEN_MAP_SIZE; i++)␊ |
120 | if (bank==vendorMap[i].bank && code==vendorMap[i].code)␊ |
121 | return vendorMap[i].name;␊ |
122 | }␊ |
123 | /* OK there is no vendor id here lets try to match the partnum if it exists */␊ |
124 | if (strstr(slot->PartNo,"GU332") == slot->PartNo) // Unifosa fingerprint␊ |
125 | return "Unifosa";␊ |
126 | return "NoName";␊ |
127 | }␊ |
128 | ␊ |
129 | /** Get Default Memory Module Speed (no overclocking handled) */␊ |
130 | int getDDRspeedMhz(const char * spd)␊ |
131 | {␊ |
132 | if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3) { ␊ |
133 | switch(spd[12]) {␊ |
134 | case 0x0f:␊ |
135 | return 1066;␊ |
136 | case 0x0c:␊ |
137 | return 1333;␊ |
138 | case 0x0a:␊ |
139 | return 1600;␊ |
140 | case 0x14:␊ |
141 | default:␊ |
142 | return 800;␊ |
143 | }␊ |
144 | } ␊ |
145 | else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2) {␊ |
146 | switch(spd[9]) {␊ |
147 | case 0x50:␊ |
148 | return 400;␊ |
149 | case 0x3d:␊ |
150 | return 533;␊ |
151 | case 0x30:␊ |
152 | return 667;␊ |
153 | case 0x25:␊ |
154 | default:␊ |
155 | return 800;␊ |
156 | }␊ |
157 | }␊ |
158 | return 800; // default freq for unknown types␊ |
159 | }␊ |
160 | ␊ |
161 | #define UIS(a) ((uint32_t)spd[a])␊ |
162 | ␊ |
163 | /** Get DDR3 or DDR2 serial number, 0 most of the times, always return a valid ptr */␊ |
164 | const char *getDDRSerial(const char* spd)␊ |
165 | {␊ |
166 | static char asciiSerial[16];␊ |
167 | static uint8_t serialnum=0;␊ |
168 | uint32_t ret=0;␊ |
169 | ␊ |
170 | if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3) {// DDR3␊ |
171 | ret = UIS(122) | (UIS(123)<<8) | (UIS(124)<<16) | ((UIS(125)&0x7f)<<24);␊ |
172 | }␊ |
173 | else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2) { // DDR2 or DDR␊ |
174 | ret = UIS(95) | (UIS(96)<<8) | (UIS(97)<<16) | ((UIS(98)&0x7f)<<24);␊ |
175 | }␊ |
176 | ␊ |
177 | if (!ret) sprintf(asciiSerial, "10000000%d", serialnum++); ␊ |
178 | else sprintf(asciiSerial, "%d", ret); ␊ |
179 | ␊ |
180 | return asciiSerial;␊ |
181 | }␊ |
182 | ␊ |
183 | /** Get DDR3 or DDR2 Part Number, always return a valid ptr */␊ |
184 | const char * getDDRPartNum(const char* spd)␊ |
185 | {␊ |
186 | const char * sPart = NULL;␊ |
187 | int i;␊ |
188 | bool bZero = false;␊ |
189 | ␊ |
190 | if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3)␊ |
191 | sPart = &spd[128];␊ |
192 | else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2)␊ |
193 | sPart = &spd[73];␊ |
194 | if (sPart) { // Check that the spd part name is zero terminated and that it is ascii:␊ |
195 | for (i=0; i<32; i++) {␊ |
196 | if (sPart[i]==0) {␊ |
197 | bZero = true;␊ |
198 | break;␊ |
199 | }␊ |
200 | else if ( !isascii(sPart[i]) ) {␊ |
201 | sPart = NULL;␊ |
202 | break;␊ |
203 | }␊ |
204 | }␊ |
205 | }␊ |
206 | return ( sPart==NULL || !(*sPart) || !bZero ) ? ␊ |
207 | "N/A" : sPart;␊ |
208 | }␊ |
209 | ␊ |
210 | int mapping []= {0,2,1,3,4,6,5,7,8,10,9,11};␊ |
211 | ␊ |
212 | /** Read from smbus the SPD content and interpret it for detecting memory attributes */␊ |
213 | static void read_smb_intel(pci_dt_t *smbus_dev)␊ |
214 | { ␊ |
215 | int i, x, speed;␊ |
216 | uint8_t spd_size, spd_type;␊ |
217 | uint32_t base;␊ |
218 | bool dump = false;␊ |
219 | RamSlotInfo_t* slot;␊ |
220 | ␊ |
221 | base = pci_config_read16(smbus_dev->dev.addr, 0x20) & 0xFFFE;␊ |
222 | DBG("Scanning smbus_dev <%04x, %04x> ...\n",smbus_dev->vendor_id, smbus_dev->device_id);␊ |
223 | ␊ |
224 | getBoolForKey("DumpSPD", &dump, &bootInfo->bootConfig);␊ |
225 | bool fullBanks = // needed at least for laptops␊ |
226 | Platform.DMI.MemoryModules == Platform.DMI.MaxMemorySlots;␊ |
227 | // Search MAX_RAM_SLOTS slots␊ |
228 | for (i = 0; i < MAX_RAM_SLOTS; i++){␊ |
229 | slot = &Platform.RAM.DIMM[i];␊ |
230 | spd_size = smb_read_byte_intel(base, 0x50 + i, 0);␊ |
231 | ␊ |
232 | // Check spd is present␊ |
233 | if (spd_size && (spd_size != 0xff) ) {␊ |
234 | slot->spd = malloc(spd_size);␊ |
235 | if (slot->spd == NULL) continue;␊ |
236 | ␊ |
237 | slot->InUse = true;␊ |
238 | ␊ |
239 | bzero(slot->spd, spd_size);␊ |
240 | ␊ |
241 | // Copy spd data into buffer␊ |
242 | for (x = 0; x < spd_size; x++)␊ |
243 | slot->spd[x] = smb_read_byte_intel(base, 0x50 + i, x);␊ |
244 | ␊ |
245 | switch (slot->spd[SPD_MEMORY_TYPE]) {␊ |
246 | case SPD_MEMORY_TYPE_SDRAM_DDR2:␊ |
247 | ␊ |
248 | slot->ModuleSize = ((1 << (slot->spd[SPD_NUM_ROWS] & 0x0f) + (slot->spd[SPD_NUM_COLUMNS] & 0x0f) - 17) * ␊ |
249 | ((slot->spd[SPD_NUM_DIMM_BANKS] & 0x7) + 1) * slot->spd[SPD_NUM_BANKS_PER_SDRAM]);␊ |
250 | break;␊ |
251 | ␊ |
252 | case SPD_MEMORY_TYPE_SDRAM_DDR3:␊ |
253 | ␊ |
254 | slot->ModuleSize = ((slot->spd[4] & 0x0f) + 28 ) + ((slot->spd[8] & 0x7) + 3 );␊ |
255 | slot->ModuleSize -= (slot->spd[7] & 0x7) + 25;␊ |
256 | slot->ModuleSize = ((1 << slot->ModuleSize) * (((slot->spd[7] >> 3) & 0x1f) + 1));␊ |
257 | ␊ |
258 | break;␊ |
259 | }␊ |
260 | ␊ |
261 | spd_type = (slot->spd[SPD_MEMORY_TYPE] < ((char) 12) ? slot->spd[SPD_MEMORY_TYPE] : 0);␊ |
262 | slot->Type = spd_mem_to_smbios[spd_type];␊ |
263 | slot->PartNo = strdup(getDDRPartNum(slot->spd) );␊ |
264 | slot->Vendor = strdup(getVendorName(slot) );␊ |
265 | slot->SerialNo = strdup(getDDRSerial(slot->spd));␊ |
266 | ␊ |
267 | // determine spd speed␊ |
268 | speed = getDDRspeedMhz(slot->spd);␊ |
269 | if (speed > slot->Frequency) slot->Frequency = speed; // just in case dmi wins on spd␊ |
270 | if(dump) {␊ |
271 | printf("Slot %d Type %d %dMB (%s) %dMHz Vendor=%s, PartNo=%s SerialNo=%s\n", ␊ |
272 | i, ␊ |
273 | (int)slot->Type,␊ |
274 | slot->ModuleSize, ␊ |
275 | spd_memory_types[spd_type],␊ |
276 | slot->Frequency,␊ |
277 | slot->Vendor,␊ |
278 | slot->PartNo,␊ |
279 | slot->SerialNo); ␊ |
280 | dumpPhysAddr("spd content: ",slot->spd, spd_size);␊ |
281 | getc();␊ |
282 | }␊ |
283 | }␊ |
284 | ␊ |
285 | // laptops sometimes show slot 0 and 2 with slot 1 empty when only 2 slots are presents so:␊ |
286 | Platform.DMI.DIMM[i]= ␊ |
287 | i>0 && Platform.RAM.DIMM[1].InUse==false && fullBanks && Platform.DMI.MaxMemorySlots==2 ? ␊ |
288 | mapping[i] : i; // for laptops case, mapping setup would need to be more generic than this␊ |
289 | ␊ |
290 | if (slot->spd) {␊ |
291 | free(slot->spd);␊ |
292 | slot->spd = NULL;␊ |
293 | }␊ |
294 | ␊ |
295 | } // for␊ |
296 | }␊ |
297 | ␊ |
298 | static struct smbus_controllers_t smbus_controllers[] = {␊ |
299 | ␊ |
300 | ␉{0x8086, 0x269B, "ESB2", read_smb_intel },␊ |
301 | ␉{0x8086, 0x25A4, "6300ESB", read_smb_intel },␊ |
302 | ␉{0x8086, 0x24C3, "ICH4", read_smb_intel },␊ |
303 | ␉{0x8086, 0x24D3, "ICH5", read_smb_intel },␊ |
304 | ␉{0x8086, 0x266A, "ICH6", read_smb_intel },␊ |
305 | ␉{0x8086, 0x27DA, "ICH7", read_smb_intel },␊ |
306 | ␉{0x8086, 0x283E, "ICH8", read_smb_intel },␊ |
307 | ␉{0x8086, 0x2930, "ICH9", read_smb_intel },␉␊ |
308 | ␉{0x8086, 0x3A30, "ICH10R", read_smb_intel },␊ |
309 | ␉{0x8086, 0x3A60, "ICH10B", read_smb_intel },␊ |
310 | ␉{0x8086, 0x3B30, "P55", read_smb_intel },␊ |
311 | ␉{0x8086, 0x5032, "EP80579", read_smb_intel }␊ |
312 | ␊ |
313 | };␊ |
314 | ␊ |
315 | // initial call : pci_dt = root_pci_dev;␊ |
316 | // find_and_read_smbus_controller(root_pci_dev);␊ |
317 | bool find_and_read_smbus_controller(pci_dt_t* pci_dt)␊ |
318 | {␊ |
319 | pci_dt_t␉*current = pci_dt;␊ |
320 | int i;␊ |
321 | ␊ |
322 | while (current) {␊ |
323 | #if DEBUG_SPD␊ |
324 | printf("%02x:%02x.%x [%04x] [%04x:%04x] :: %s\n", ␊ |
325 | current->dev.bits.bus, current->dev.bits.dev, current->dev.bits.func, ␊ |
326 | current->class_id, current->vendor_id, current->device_id, ␊ |
327 | get_pci_dev_path(current));␊ |
328 | #endif␊ |
329 | ␉for ( i = 0; i < sizeof(smbus_controllers) / sizeof(smbus_controllers[0]); i++ )␊ |
330 | {␊ |
331 | if (current->vendor_id == smbus_controllers[i].vendor &&␊ |
332 | current->device_id == smbus_controllers[i].device)␊ |
333 | {␊ |
334 | smbus_controllers[i].read_smb(current); // read smb␊ |
335 | return true;␊ |
336 | } ␊ |
337 | }␊ |
338 | find_and_read_smbus_controller(current->children);␊ |
339 | current = current->next;␊ |
340 | }␊ |
341 | return false; // not found␊ |
342 | }␊ |
343 | ␊ |
344 | void scan_spd(PlatformInfo_t *p)␊ |
345 | {␊ |
346 | find_and_read_smbus_controller(root_pci_dev);␊ |
347 | }␊ |
348 |