f7c6b5af25dfe49208ff94a9a59205d52cb86df7
[coreboot.git] / src / northbridge / intel / i82830 / smihandler.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2010 coresystems GmbH
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; version 2 of
9  * the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21
22 #include <types.h>
23 #include <string.h>
24 #include <arch/io.h>
25 #include <arch/romcc_io.h>
26 #include <console/console.h>
27 #include <cpu/x86/cache.h>
28 #include <cpu/x86/smm.h>
29 #include <device/pci_def.h>
30 #include "i82830.h"
31
32 extern unsigned char *mbi;
33 extern u32 mbi_len;
34
35 // #define DEBUG_SMI_I82830
36
37 /* If YABEL is enabled and it's not running at 0x00000000, we have to add some
38  * offset to all our mbi object memory accesses
39  */
40 #if CONFIG_PCI_OPTION_ROM_RUN_YABEL && !CONFIG_YABEL_DIRECTHW
41 #define OBJ_OFFSET CONFIG_YABEL_VIRTMEM_LOCATION
42 #else
43 #define OBJ_OFFSET 0x00000
44 #endif
45
46 /* I830M */
47 #define SMRAM           0x90
48 #define   D_OPEN        (1 << 6)
49 #define   D_CLS         (1 << 5)
50 #define   D_LCK         (1 << 4)
51 #define   G_SMRANE      (1 << 3)
52 #define   C_BASE_SEG    ((0 << 2) | (1 << 1) | (0 << 0))
53
54
55 typedef struct {
56         u32 mhid;
57         u32 function;
58         u32 retsts;
59         u32 rfu;
60 } __attribute__((packed)) banner_id_t;
61
62 #define MSH_OK                  0x0000
63 #define MSH_OK_RESTART          0x0001
64 #define MSH_FWH_ERR             0x00ff
65 #define MSH_IF_BAD_ID           0x0100
66 #define MSH_IF_BAD_FUNC         0x0101
67 #define MSH_IF_MBI_CORRUPT      0x0102
68 #define MSH_IF_BAD_HANDLE       0x0103
69 #define MSH_ALRDY_ATCHED        0x0104
70 #define MSH_NOT_ATCHED          0x0105
71 #define MSH_IF                  0x0106
72 #define MSH_IF_INVADDR          0x0107
73 #define MSH_IF_UKN_TYPE         0x0108
74 #define MSH_IF_NOT_FOUND        0x0109
75 #define MSH_IF_NO_KEY           0x010a
76 #define MSH_IF_BUF_SIZE         0x010b
77 #define MSH_IF_NOT_PENDING      0x010c
78
79 #ifdef DEBUG_SMI_I82830
80 static void
81 dump(u8 * addr, u32 len)
82 {
83         printk(BIOS_DEBUG, "\n%s(%p, %x):\n", __func__, addr, len);
84         while (len) {
85                 unsigned int tmpCnt = len;
86                 unsigned char x;
87                 if (tmpCnt > 8)
88                         tmpCnt = 8;
89                 printk(BIOS_DEBUG, "\n%p: ", addr);
90                 // print hex
91                 while (tmpCnt--) {
92                         x = *addr++;
93                         printk(BIOS_DEBUG, "%02x ", x);
94                 }
95                 tmpCnt = len;
96                 if (tmpCnt > 8)
97                         tmpCnt = 8;
98                 len -= tmpCnt;
99                 //reset addr ptr to print ascii
100                 addr = addr - tmpCnt;
101                 // print ascii
102                 while (tmpCnt--) {
103                         x = *addr++;
104                         if ((x < 32) || (x >= 127)) {
105                                 //non-printable char
106                                 x = '.';
107                         }
108                         printk(BIOS_DEBUG, "%c", x);
109                 }
110         }
111         printk(BIOS_DEBUG, "\n");
112 }
113 #endif
114
115 typedef struct {
116         banner_id_t banner;
117         u16 versionmajor;
118         u16 versionminor;
119         u32 smicombuffersize;
120 } __attribute__((packed)) version_t;
121
122 typedef struct {
123         u16 header_id;
124         u16 attributes;
125         u16 size;
126         u8  name_len;
127         u8 reserved;
128         u32 type;
129         u32 header_ext;
130         u8 name[0];
131 } __attribute__((packed)) mbi_header_t;
132
133 typedef struct {
134         banner_id_t banner;
135         u64 handle;
136         u32 objnum;
137         mbi_header_t header;
138 } __attribute__((packed)) obj_header_t;
139
140 typedef struct {
141         banner_id_t banner;
142         u64 handle;
143         u32 objnum;
144         u32 start;
145         u32 numbytes;
146         u32 buflen;
147         u32 buffer;
148 } __attribute__((packed)) get_object_t;
149
150 static void mbi_call(u8 subf, banner_id_t *banner_id)
151 {
152 #ifdef DEBUG_SMI_I82830
153         printk(BIOS_DEBUG, "MBI\n");
154         printk(BIOS_DEBUG, "|- sub function %x\n", subf);
155         printk(BIOS_DEBUG, "|- banner id @ %x\n", (u32)banner_id);
156         printk(BIOS_DEBUG, "|  |- mhid %x\n", banner_id->mhid);
157         printk(BIOS_DEBUG, "|  |- function %x\n", banner_id->function);
158         printk(BIOS_DEBUG, "|  |- return status %x\n", banner_id->retsts);
159         printk(BIOS_DEBUG, "|  |- rfu %x\n", banner_id->rfu);
160 #endif
161
162         switch(banner_id->function) {
163         case 0x0001: {
164                 version_t *version;
165                 printk(BIOS_DEBUG, "|- MBI_QueryInterface\n");
166                 version = (version_t *)banner_id;
167                 version->banner.retsts = MSH_OK;
168                 version->versionmajor=1;
169                 version->versionminor=3;
170                 version->smicombuffersize=0x1000;
171                 break;
172         }
173         case 0x0002:
174                 printk(BIOS_DEBUG, "|- MBI_Attach\n");
175                 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
176                 break;
177         case 0x0003:
178                 printk(BIOS_DEBUG, "|- MBI_Detach\n");
179                 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
180                 break;
181         case 0x0201: {
182                 obj_header_t *obj_header = (obj_header_t *)banner_id;
183                 mbi_header_t *mbi_header = NULL;
184                 printk(BIOS_DEBUG, "|- MBI_GetObjectHeader\n");
185                 printk(BIOS_DEBUG, "|  |- objnum = %d\n", obj_header->objnum);
186
187                 int i, count=0;
188                 obj_header->banner.retsts = MSH_IF_NOT_FOUND;
189
190                 for (i=0; i<mbi_len;) {
191                         int len;
192
193                         if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
194                                 i+=16;
195                                 continue;
196                         }
197
198                         mbi_header = (mbi_header_t *)&mbi[i];
199                         len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16); 
200
201                         if (obj_header->objnum == count) {
202 #ifdef DEBUG_SMI_I82830
203                                 if (mbi_header->name_len == 0xff) {
204                                         printk(BIOS_DEBUG, "|  |- corrupt.\n");
205                                         break;
206                                 }
207 #endif
208                                 int headerlen = ALIGN(sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16);
209 #ifdef DEBUG_SMI_I82830
210                                 printk(BIOS_DEBUG, "|  |- headerlen = %d\n", headerlen);
211 #endif
212                                 memcpy(&obj_header->header, mbi_header, headerlen);
213                                 obj_header->banner.retsts = MSH_OK;
214                                 printk(BIOS_DEBUG, "|     |- MBI module '");
215                                 int j;
216                                 for (j=0; j < mbi_header->name_len && mbi_header->name[j]; j++)
217                                         printk(BIOS_DEBUG, "%c",  mbi_header->name[j]);
218                                 printk(BIOS_DEBUG, "' found.\n");
219 #ifdef DEBUG_SMI_I82830
220                                 dump((u8 *)banner_id, sizeof(obj_header_t) + ALIGN(mbi_header->name_len, 16));
221 #endif
222                                 break;
223                         }
224                         i += len;
225                         count++;
226                 }
227                 if (obj_header->banner.retsts == MSH_IF_NOT_FOUND)
228                         printk(BIOS_DEBUG, "|     |- MBI object #%d not found.\n", obj_header->objnum);
229                 break;
230         }
231         case 0x0203: {
232                 get_object_t *getobj = (get_object_t *)banner_id;
233                 mbi_header_t *mbi_header = NULL;
234                 printk(BIOS_DEBUG, "|- MBI_GetObject\n");
235 #ifdef DEBUG_SMI_I82830
236                 printk(BIOS_DEBUG, "|  |- handle = %016Lx\n", getobj->handle);
237 #endif
238                 printk(BIOS_DEBUG, "|  |- objnum = %d\n", getobj->objnum);
239                 printk(BIOS_DEBUG, "|  |- start = %x\n", getobj->start);
240                 printk(BIOS_DEBUG, "|  |- numbytes = %x\n", getobj->numbytes);
241                 printk(BIOS_DEBUG, "|  |- buflen = %x\n", getobj->buflen);
242                 printk(BIOS_DEBUG, "|  |- buffer = %x\n", getobj->buffer);
243
244                 int i, count=0;
245                 getobj->banner.retsts = MSH_IF_NOT_FOUND;
246
247                 for (i=0; i< mbi_len;) {
248                         int headerlen, objectlen;
249
250                         if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
251                                 i+=16;
252                                 continue;
253                         }
254
255                         mbi_header = (mbi_header_t *)&mbi[i];
256                         headerlen = ALIGN(sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16);
257                         objectlen = ALIGN((mbi_header->size * 16), 16);
258
259                         if (getobj->objnum == count) {
260                                 printk(BIOS_DEBUG, "|  |- len = %x\n", headerlen + objectlen);
261
262                                 memcpy((void *)(getobj->buffer + OBJ_OFFSET),
263                                                 ((char *)mbi_header) + headerlen, (objectlen > getobj->buflen) ? getobj->buflen : objectlen);
264
265                                 getobj->banner.retsts = MSH_OK;
266 #ifdef DEBUG_SMI_I82830
267                                 dump((u8 *)banner_id, sizeof(*getobj));
268                                 dump((u8 *)getobj->buffer + OBJ_OFFSET, objectlen);
269 #endif
270                                 break;
271                         }
272                         i += (headerlen + objectlen);
273                         count++;
274                 }
275                 if (getobj->banner.retsts == MSH_IF_NOT_FOUND)
276                         printk(BIOS_DEBUG, "MBI module %d not found.\n", getobj->objnum);
277                 break;
278         }
279         default:
280                 printk(BIOS_DEBUG, "|- function %x\n", banner_id->function);
281                 printk(BIOS_DEBUG, "|  |- Unknown Function!\n");
282                 break;
283         }
284         printk(BIOS_DEBUG, "\n");
285         //dump(banner_id, 0x20);
286 }
287
288 #define SMI_IFC_SUCCESS             1
289 #define SMI_IFC_FAILURE_GENERIC     0
290 #define SMI_IFC_FAILURE_INVALID     2
291 #define SMI_IFC_FAILURE_CRITICAL    4
292 #define SMI_IFC_FAILURE_NONCRITICAL 6
293
294 #define PC10    0x10
295 #define PC11    0x11
296 #define PC12    0x12
297 #define PC13    0x13
298
299 static void smi_interface_call(void)
300 {
301         u32 mmio = pci_read_config32(PCI_DEV(0, 0x02, 0), 0x14);
302         // mmio &= 0xfff80000;
303         // printk(BIOS_DEBUG, "mmio=%x\n", mmio);
304         u16 swsmi = pci_read_config16(PCI_DEV(0, 0x02, 0), 0xe0);
305
306         if (!(swsmi & 1))
307                 return;
308
309         swsmi &= ~(1 << 0); // clear SMI toggle
310
311         switch ((swsmi>>1) & 0xf) {
312         case 0:
313                 printk(BIOS_DEBUG, "Interface Function Presence Test.\n");
314                 swsmi = 0;
315                 swsmi &= ~(7 << 5); // Exit: Result
316                 swsmi |= (SMI_IFC_SUCCESS << 5);
317                 swsmi &= 0xff;
318                 swsmi |= (PC13 << 8);
319                 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
320                 // write magic
321                 write32(mmio + 0x71428, 0x494e5443);
322                 return;
323         case 4:
324                 printk(BIOS_DEBUG, "Get BIOS Data.\n");
325                 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
326                 break;
327         case 5:
328                 printk(BIOS_DEBUG, "Call MBI Functions.\n");
329                 mbi_call(swsmi >> 8, (banner_id_t *)((read32(mmio + 0x71428) & 0x000fffff) + OBJ_OFFSET) );
330                 // swsmi = 0x0000;
331                 swsmi &= ~(7 << 5); // Exit: Result
332                 swsmi |= (SMI_IFC_SUCCESS << 5);
333                 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
334                 return;
335         case 6:
336                 printk(BIOS_DEBUG, "System BIOS Callbacks.\n");
337                 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
338                 break;
339         default:
340                 printk(BIOS_DEBUG, "Unknown SMI interface call %04x\n", swsmi);
341                 break;
342         }
343
344         swsmi &= ~(7 << 5); // Exit: Result
345         swsmi |= (SMI_IFC_FAILURE_CRITICAL << 7);
346         pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
347 }
348
349 /**
350  * @brief read and clear ERRSTS
351  * @return ERRSTS register
352  */
353 static u16 reset_err_status(void)
354 {
355         u16 reg16;
356
357         reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), ERRSTS);
358         /* set status bits are cleared by writing 1 to them */
359         pci_write_config16(PCI_DEV(0, 0x00, 0), ERRSTS, reg16);
360
361         return reg16;
362 }
363
364 static void dump_err_status(u32 errsts)
365 {
366         printk(BIOS_DEBUG, "ERRSTS: ");
367         if (errsts & (1 << 12)) printk(BIOS_DEBUG, "MBI ");
368         if (errsts & (1 <<  9)) printk(BIOS_DEBUG, "LCKF ");
369         if (errsts & (1 <<  8)) printk(BIOS_DEBUG, "DTF ");
370         if (errsts & (1 <<  5)) printk(BIOS_DEBUG, "UNSC ");
371         if (errsts & (1 <<  4)) printk(BIOS_DEBUG, "OOGF ");
372         if (errsts & (1 <<  3)) printk(BIOS_DEBUG, "IAAF ");
373         if (errsts & (1 <<  2)) printk(BIOS_DEBUG, "ITTEF ");
374         printk(BIOS_DEBUG, "\n");
375 }
376
377 void northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
378 {
379         u16 errsts;
380
381         /* We need to clear the SMI status registers, or we won't see what's
382          * happening in the following calls.
383          */
384         errsts = reset_err_status();
385         if (errsts & (1 << 12)) {
386                 smi_interface_call();
387         } else {
388                 if (errsts)
389                         dump_err_status(errsts);
390         }
391
392 }