blob: 12da69830e85c3a32a78127eed47852c1c221c2a [file] [log] [blame]
Stefan Reinauer800379f2010-03-01 08:34:19 +00001/*
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.
Stefan Reinauer800379f2010-03-01 08:34:19 +000015 */
16
17#include <types.h>
18#include <string.h>
19#include <arch/io.h>
Stefan Reinauer800379f2010-03-01 08:34:19 +000020#include <console/console.h>
21#include <cpu/x86/cache.h>
22#include <cpu/x86/smm.h>
23#include <device/pci_def.h>
24#include "i82830.h"
25
26extern unsigned char *mbi;
27extern u32 mbi_len;
28
Stefan Reinauer72f75b12010-03-01 09:09:33 +000029// #define DEBUG_SMI_I82830
Stefan Reinauer800379f2010-03-01 08:34:19 +000030
31/* If YABEL is enabled and it's not running at 0x00000000, we have to add some
32 * offset to all our mbi object memory accesses
33 */
Stefan Reinauer1d888a92011-04-21 20:24:43 +000034#if CONFIG_PCI_OPTION_ROM_RUN_YABEL && !CONFIG_YABEL_DIRECTHW
Stefan Reinauer800379f2010-03-01 08:34:19 +000035#define OBJ_OFFSET CONFIG_YABEL_VIRTMEM_LOCATION
36#else
37#define OBJ_OFFSET 0x00000
38#endif
39
40/* I830M */
41#define SMRAM 0x90
42#define D_OPEN (1 << 6)
43#define D_CLS (1 << 5)
44#define D_LCK (1 << 4)
45#define G_SMRANE (1 << 3)
46#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
47
48
49typedef struct {
50 u32 mhid;
51 u32 function;
52 u32 retsts;
53 u32 rfu;
54} __attribute__((packed)) banner_id_t;
55
56#define MSH_OK 0x0000
57#define MSH_OK_RESTART 0x0001
58#define MSH_FWH_ERR 0x00ff
59#define MSH_IF_BAD_ID 0x0100
60#define MSH_IF_BAD_FUNC 0x0101
61#define MSH_IF_MBI_CORRUPT 0x0102
62#define MSH_IF_BAD_HANDLE 0x0103
63#define MSH_ALRDY_ATCHED 0x0104
64#define MSH_NOT_ATCHED 0x0105
65#define MSH_IF 0x0106
66#define MSH_IF_INVADDR 0x0107
67#define MSH_IF_UKN_TYPE 0x0108
68#define MSH_IF_NOT_FOUND 0x0109
69#define MSH_IF_NO_KEY 0x010a
70#define MSH_IF_BUF_SIZE 0x010b
71#define MSH_IF_NOT_PENDING 0x010c
72
Stefan Reinauer72f75b12010-03-01 09:09:33 +000073#ifdef DEBUG_SMI_I82830
Stefan Reinauer800379f2010-03-01 08:34:19 +000074static void
75dump(u8 * addr, u32 len)
76{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000077 printk(BIOS_DEBUG, "\n%s(%p, %x):\n", __func__, addr, len);
Stefan Reinauer800379f2010-03-01 08:34:19 +000078 while (len) {
79 unsigned int tmpCnt = len;
80 unsigned char x;
81 if (tmpCnt > 8)
82 tmpCnt = 8;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000083 printk(BIOS_DEBUG, "\n%p: ", addr);
Stefan Reinauer800379f2010-03-01 08:34:19 +000084 // print hex
85 while (tmpCnt--) {
86 x = *addr++;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000087 printk(BIOS_DEBUG, "%02x ", x);
Stefan Reinauer800379f2010-03-01 08:34:19 +000088 }
89 tmpCnt = len;
90 if (tmpCnt > 8)
91 tmpCnt = 8;
92 len -= tmpCnt;
93 //reset addr ptr to print ascii
94 addr = addr - tmpCnt;
95 // print ascii
96 while (tmpCnt--) {
97 x = *addr++;
98 if ((x < 32) || (x >= 127)) {
99 //non-printable char
100 x = '.';
101 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000102 printk(BIOS_DEBUG, "%c", x);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000103 }
104 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000105 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000106}
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000107#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000108
109typedef struct {
110 banner_id_t banner;
111 u16 versionmajor;
112 u16 versionminor;
113 u32 smicombuffersize;
114} __attribute__((packed)) version_t;
115
116typedef struct {
117 u16 header_id;
118 u16 attributes;
119 u16 size;
120 u8 name_len;
121 u8 reserved;
122 u32 type;
123 u32 header_ext;
124 u8 name[0];
125} __attribute__((packed)) mbi_header_t;
126
127typedef struct {
128 banner_id_t banner;
129 u64 handle;
130 u32 objnum;
131 mbi_header_t header;
132} __attribute__((packed)) obj_header_t;
133
134typedef struct {
135 banner_id_t banner;
136 u64 handle;
137 u32 objnum;
138 u32 start;
139 u32 numbytes;
140 u32 buflen;
141 u32 buffer;
142} __attribute__((packed)) get_object_t;
143
144static void mbi_call(u8 subf, banner_id_t *banner_id)
145{
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000146#ifdef DEBUG_SMI_I82830
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000147 printk(BIOS_DEBUG, "MBI\n");
148 printk(BIOS_DEBUG, "|- sub function %x\n", subf);
149 printk(BIOS_DEBUG, "|- banner id @ %x\n", (u32)banner_id);
150 printk(BIOS_DEBUG, "| |- mhid %x\n", banner_id->mhid);
151 printk(BIOS_DEBUG, "| |- function %x\n", banner_id->function);
152 printk(BIOS_DEBUG, "| |- return status %x\n", banner_id->retsts);
153 printk(BIOS_DEBUG, "| |- rfu %x\n", banner_id->rfu);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000154#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000155
156 switch(banner_id->function) {
157 case 0x0001: {
158 version_t *version;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000159 printk(BIOS_DEBUG, "|- MBI_QueryInterface\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000160 version = (version_t *)banner_id;
161 version->banner.retsts = MSH_OK;
162 version->versionmajor=1;
163 version->versionminor=3;
164 version->smicombuffersize=0x1000;
165 break;
166 }
167 case 0x0002:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000168 printk(BIOS_DEBUG, "|- MBI_Attach\n");
169 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000170 break;
171 case 0x0003:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000172 printk(BIOS_DEBUG, "|- MBI_Detach\n");
173 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000174 break;
175 case 0x0201: {
176 obj_header_t *obj_header = (obj_header_t *)banner_id;
177 mbi_header_t *mbi_header = NULL;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000178 printk(BIOS_DEBUG, "|- MBI_GetObjectHeader\n");
179 printk(BIOS_DEBUG, "| |- objnum = %d\n", obj_header->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000180
181 int i, count=0;
182 obj_header->banner.retsts = MSH_IF_NOT_FOUND;
183
Stefan Reinauerd9466492010-04-11 20:04:50 +0000184 for (i=0; i<mbi_len;) {
Stefan Reinauer800379f2010-03-01 08:34:19 +0000185 int len;
186
187 if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
188 i+=16;
189 continue;
190 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000191
Stefan Reinauer800379f2010-03-01 08:34:19 +0000192 mbi_header = (mbi_header_t *)&mbi[i];
Stefan Reinauer5ff7c132011-10-31 12:56:45 -0700193 len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000194
Stefan Reinauer800379f2010-03-01 08:34:19 +0000195 if (obj_header->objnum == count) {
Stefan Reinauerd9466492010-04-11 20:04:50 +0000196#ifdef DEBUG_SMI_I82830
197 if (mbi_header->name_len == 0xff) {
198 printk(BIOS_DEBUG, "| |- corrupt.\n");
199 break;
200 }
201#endif
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000202 int headerlen = ALIGN(sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000203#ifdef DEBUG_SMI_I82830
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000204 printk(BIOS_DEBUG, "| |- headerlen = %d\n", headerlen);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000205#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000206 memcpy(&obj_header->header, mbi_header, headerlen);
207 obj_header->banner.retsts = MSH_OK;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000208 printk(BIOS_DEBUG, "| |- MBI module '");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000209 int j;
210 for (j=0; j < mbi_header->name_len && mbi_header->name[j]; j++)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000211 printk(BIOS_DEBUG, "%c", mbi_header->name[j]);
212 printk(BIOS_DEBUG, "' found.\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000213#ifdef DEBUG_SMI_I82830
Stefan Reinauerc56e5ad2010-05-27 15:41:15 +0000214 dump((u8 *)banner_id, sizeof(obj_header_t) + ALIGN(mbi_header->name_len, 16));
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000215#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000216 break;
217 }
218 i += len;
219 count++;
220 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000221 if (obj_header->banner.retsts == MSH_IF_NOT_FOUND)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000222 printk(BIOS_DEBUG, "| |- MBI object #%d not found.\n", obj_header->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000223 break;
224 }
225 case 0x0203: {
226 get_object_t *getobj = (get_object_t *)banner_id;
227 mbi_header_t *mbi_header = NULL;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000228 printk(BIOS_DEBUG, "|- MBI_GetObject\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000229#ifdef DEBUG_SMI_I82830
Stefan Reinauerc56e5ad2010-05-27 15:41:15 +0000230 printk(BIOS_DEBUG, "| |- handle = %016Lx\n", getobj->handle);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000231#endif
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000232 printk(BIOS_DEBUG, "| |- objnum = %d\n", getobj->objnum);
233 printk(BIOS_DEBUG, "| |- start = %x\n", getobj->start);
234 printk(BIOS_DEBUG, "| |- numbytes = %x\n", getobj->numbytes);
235 printk(BIOS_DEBUG, "| |- buflen = %x\n", getobj->buflen);
236 printk(BIOS_DEBUG, "| |- buffer = %x\n", getobj->buffer);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000237
238 int i, count=0;
239 getobj->banner.retsts = MSH_IF_NOT_FOUND;
240
241 for (i=0; i< mbi_len;) {
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000242 int headerlen, objectlen;
Stefan Reinauer800379f2010-03-01 08:34:19 +0000243
244 if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
245 i+=16;
246 continue;
247 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000248
Stefan Reinauer800379f2010-03-01 08:34:19 +0000249 mbi_header = (mbi_header_t *)&mbi[i];
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000250 headerlen = ALIGN(sizeof(mbi_header) + ALIGN(mbi_header->name_len, 16), 16);
251 objectlen = ALIGN((mbi_header->size * 16), 16);
Stefan Reinauer14e22772010-04-27 06:56:47 +0000252
Stefan Reinauer800379f2010-03-01 08:34:19 +0000253 if (getobj->objnum == count) {
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000254 printk(BIOS_DEBUG, "| |- len = %x\n", headerlen + objectlen);
255
Stefan Reinauer800379f2010-03-01 08:34:19 +0000256 memcpy((void *)(getobj->buffer + OBJ_OFFSET),
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000257 ((char *)mbi_header) + headerlen, (objectlen > getobj->buflen) ? getobj->buflen : objectlen);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000258
259 getobj->banner.retsts = MSH_OK;
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000260#ifdef DEBUG_SMI_I82830
Stefan Reinauerc56e5ad2010-05-27 15:41:15 +0000261 dump((u8 *)banner_id, sizeof(*getobj));
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000262 dump((u8 *)getobj->buffer + OBJ_OFFSET, objectlen);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000263#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000264 break;
265 }
Joseph Smith7b2e2b92010-05-27 22:47:13 +0000266 i += (headerlen + objectlen);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000267 count++;
268 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000269 if (getobj->banner.retsts == MSH_IF_NOT_FOUND)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000270 printk(BIOS_DEBUG, "MBI module %d not found.\n", getobj->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000271 break;
272 }
273 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000274 printk(BIOS_DEBUG, "|- function %x\n", banner_id->function);
275 printk(BIOS_DEBUG, "| |- Unknown Function!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000276 break;
277 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000278 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000279 //dump(banner_id, 0x20);
280}
281
282#define SMI_IFC_SUCCESS 1
283#define SMI_IFC_FAILURE_GENERIC 0
284#define SMI_IFC_FAILURE_INVALID 2
285#define SMI_IFC_FAILURE_CRITICAL 4
286#define SMI_IFC_FAILURE_NONCRITICAL 6
287
288#define PC10 0x10
289#define PC11 0x11
290#define PC12 0x12
291#define PC13 0x13
292
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000293static void smi_interface_call(void)
Stefan Reinauer800379f2010-03-01 08:34:19 +0000294{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800295 u8 *mmio = (u8 *)pci_read_config32(PCI_DEV(0, 0x02, 0), 0x14);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000296 // mmio &= 0xfff80000;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000297 // printk(BIOS_DEBUG, "mmio=%x\n", mmio);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000298 u16 swsmi = pci_read_config16(PCI_DEV(0, 0x02, 0), 0xe0);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000299
300 if (!(swsmi & 1))
301 return;
302
303 swsmi &= ~(1 << 0); // clear SMI toggle
304
305 switch ((swsmi>>1) & 0xf) {
306 case 0:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000307 printk(BIOS_DEBUG, "Interface Function Presence Test.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000308 swsmi = 0;
309 swsmi &= ~(7 << 5); // Exit: Result
310 swsmi |= (SMI_IFC_SUCCESS << 5);
311 swsmi &= 0xff;
312 swsmi |= (PC13 << 8);
313 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000314 // write magic
Stefan Reinauer800379f2010-03-01 08:34:19 +0000315 write32(mmio + 0x71428, 0x494e5443);
316 return;
317 case 4:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000318 printk(BIOS_DEBUG, "Get BIOS Data.\n");
319 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000320 break;
321 case 5:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000322 printk(BIOS_DEBUG, "Call MBI Functions.\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000323 mbi_call(swsmi >> 8, (banner_id_t *)((read32(mmio + 0x71428) & 0x000fffff) + OBJ_OFFSET) );
Stefan Reinauer800379f2010-03-01 08:34:19 +0000324 // swsmi = 0x0000;
325 swsmi &= ~(7 << 5); // Exit: Result
326 swsmi |= (SMI_IFC_SUCCESS << 5);
327 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
328 return;
329 case 6:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000330 printk(BIOS_DEBUG, "System BIOS Callbacks.\n");
331 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000332 break;
333 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000334 printk(BIOS_DEBUG, "Unknown SMI interface call %04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000335 break;
336 }
337
338 swsmi &= ~(7 << 5); // Exit: Result
339 swsmi |= (SMI_IFC_FAILURE_CRITICAL << 7);
340 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
341}
342
343/**
344 * @brief read and clear ERRSTS
345 * @return ERRSTS register
346 */
347static u16 reset_err_status(void)
348{
349 u16 reg16;
350
351 reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), ERRSTS);
352 /* set status bits are cleared by writing 1 to them */
353 pci_write_config16(PCI_DEV(0, 0x00, 0), ERRSTS, reg16);
354
355 return reg16;
356}
357
358static void dump_err_status(u32 errsts)
359{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000360 printk(BIOS_DEBUG, "ERRSTS: ");
361 if (errsts & (1 << 12)) printk(BIOS_DEBUG, "MBI ");
362 if (errsts & (1 << 9)) printk(BIOS_DEBUG, "LCKF ");
363 if (errsts & (1 << 8)) printk(BIOS_DEBUG, "DTF ");
364 if (errsts & (1 << 5)) printk(BIOS_DEBUG, "UNSC ");
365 if (errsts & (1 << 4)) printk(BIOS_DEBUG, "OOGF ");
366 if (errsts & (1 << 3)) printk(BIOS_DEBUG, "IAAF ");
367 if (errsts & (1 << 2)) printk(BIOS_DEBUG, "ITTEF ");
368 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000369}
370
371void northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
372{
373 u16 errsts;
374
375 /* We need to clear the SMI status registers, or we won't see what's
376 * happening in the following calls.
377 */
378 errsts = reset_err_status();
379 if (errsts & (1 << 12)) {
380 smi_interface_call();
381 } else {
382 if (errsts)
383 dump_err_status(errsts);
384 }
385
386}