1 /* Copyright 2016 IBM 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 * 15 */ 16 17 #include <assert.h> 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <getopt.h> 21 #include <limits.h> 22 #include <poll.h> 23 #include <stdbool.h> 24 #include <stdint.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <syslog.h> 29 #include <signal.h> 30 #include <sys/ioctl.h> 31 #include <sys/mman.h> 32 #include <sys/stat.h> 33 #include <sys/timerfd.h> 34 #include <sys/types.h> 35 #include <time.h> 36 #include <unistd.h> 37 #include <inttypes.h> 38 39 #include <mtd/mtd-abi.h> 40 41 #include <linux/aspeed-lpc-ctrl.h> 42 43 #include "mbox.h" 44 #include "common.h" 45 46 #define LPC_CTRL_PATH "/dev/aspeed-lpc-ctrl" 47 48 #define MBOX_FD 0 49 #define LPC_CTRL_FD 1 50 #define MTD_FD 2 51 #define TOTAL_FDS 3 52 53 #define ALIGN_UP(_v, _a) (((_v) + (_a) - 1) & ~((_a) - 1)) 54 55 #define MSG_OUT(f_, ...) do { if (verbosity != MBOX_LOG_NONE) { mbox_log(LOG_INFO, f_, ##__VA_ARGS__); } } while(0) 56 #define MSG_ERR(f_, ...) do { if (verbosity != MBOX_LOG_NONE) { mbox_log(LOG_ERR, f_, ##__VA_ARGS__); } } while(0) 57 58 #define BOOT_HICR7 0x30000e00U 59 #define BOOT_HICR8 0xfe0001ffU 60 61 struct mbox_context { 62 struct pollfd fds[TOTAL_FDS]; 63 void *lpc_mem; 64 uint32_t base; 65 uint32_t size; 66 uint32_t pgsize; 67 bool dirty; 68 uint32_t dirtybase; 69 uint32_t dirtysize; 70 struct mtd_info_user mtd_info; 71 }; 72 73 static int running = 1; 74 static int sighup = 0; 75 76 static int point_to_flash(struct mbox_context *context) 77 { 78 struct aspeed_lpc_ctrl_mapping map; 79 int r = 0; 80 81 /* 82 * Point it to the real flash for sanity. 83 * 84 * This function assumes 32MB of flash which means that that 85 * hostboot expects flash to be at 0x0e000000 - 0x0fffffff on the 86 * LPC bus. If the machine actually has 64MB of flash then the 87 * map.addr should be 0x0c000000. TODO 88 * 89 * Until hostboot learns how to talk to this daemon this hardcode will 90 * get hostboot going. Furthermore, when hostboot does learn to talk 91 * then this mapping is unnecessary and this code should be removed. 92 */ 93 94 map.addr = 0x0e000000; 95 map.size = 0x02000000; /* 32MB */ 96 map.offset = 0; 97 map.window_type = ASPEED_LPC_CTRL_WINDOW_FLASH; 98 map.window_id = 0; /* Theres only one */ 99 100 MSG_OUT("Pointing HOST LPC bus at the actual flash\n"); 101 MSG_OUT("Assuming 32MB of flash: HOST LPC 0x%08x\n", map.addr); 102 103 if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map) == -1) { 104 r = -errno; 105 MSG_ERR("Couldn't MAP the host LPC bus to the platform flash\n"); 106 } 107 108 return r; 109 } 110 111 static int flash_write(struct mbox_context *context, uint32_t pos, uint32_t len) 112 { 113 int rc; 114 struct erase_info_user erase_info = { 115 .start = pos, 116 }; 117 118 assert(context); 119 120 erase_info.length = ALIGN_UP(len, context->mtd_info.erasesize); 121 122 MSG_OUT("Erasing 0x%08x for 0x%08x (aligned: 0x%08x)\n", pos, len, erase_info.length); 123 if (ioctl(-context->fds[MTD_FD].fd, MEMERASE, &erase_info) == -1) { 124 MSG_ERR("Couldn't MEMERASE ioctl, flash write lost: %s\n", strerror(errno)); 125 return -1; 126 } 127 128 if (lseek(-context->fds[MTD_FD].fd, pos, SEEK_SET) == (off_t) -1) { 129 MSG_ERR("Couldn't seek to 0x%08x into MTD, flash write lost: %s\n", pos, strerror(errno)); 130 return -1; 131 } 132 133 while (erase_info.length) { 134 rc = write(-context->fds[MTD_FD].fd, context->lpc_mem + pos, erase_info.length); 135 if (rc == -1) { 136 MSG_ERR("Couldn't write to flash! Flash write lost: %s\n", strerror(errno)); 137 return -1; 138 } 139 erase_info.length -= rc; 140 pos += rc; 141 } 142 143 return 0; 144 } 145 146 /* TODO: Add come consistency around the daemon exiting and either 147 * way, ensuring it responds. 148 * I'm in favour of an approach where it does its best to stay alive 149 * and keep talking, the hacky prototype was written the other way. 150 * This function is now inconsistent 151 */ 152 static int dispatch_mbox(struct mbox_context *context) 153 { 154 int r = 0; 155 int len; 156 off_t pos; 157 uint8_t byte; 158 union mbox_regs resp, req = { 0 }; 159 uint16_t sizepg, basepg, dirtypg; 160 uint32_t dirtycount; 161 struct aspeed_lpc_ctrl_mapping map; 162 163 assert(context); 164 165 map.addr = context->base; 166 map.size = context->size; 167 map.offset = 0; 168 map.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY; 169 map.window_id = 0; /* Theres only one */ 170 171 MSG_OUT("Dispatched to mbox\n"); 172 r = read(context->fds[MBOX_FD].fd, &req, sizeof(req.raw)); 173 if (r < 0) { 174 r = -errno; 175 MSG_ERR("Couldn't read: %s\n", strerror(errno)); 176 goto out; 177 } 178 if (r < sizeof(req.msg)) { 179 MSG_ERR("Short read: %d expecting %zu\n", r, sizeof(req.msg)); 180 r = -1; 181 goto out; 182 } 183 184 /* We are NOT going to update the last two 'status' bytes */ 185 memcpy(&resp, &req, sizeof(req.msg)); 186 187 sizepg = context->size >> context->pgsize; 188 basepg = context->base >> context->pgsize; 189 MSG_OUT("Got data in with command %d\n", req.msg.command); 190 switch (req.msg.command) { 191 case MBOX_C_RESET_STATE: 192 /* Called by early hostboot? TODO */ 193 resp.msg.response = MBOX_R_SUCCESS; 194 r = point_to_flash(context); 195 if (r) { 196 resp.msg.response = MBOX_R_SYSTEM_ERROR; 197 MSG_ERR("Couldn't point the LPC BUS back to actual flash\n"); 198 } 199 break; 200 case MBOX_C_GET_MBOX_INFO: 201 /* TODO Freak if data.data[0] isn't 1 */ 202 resp.msg.data[0] = 1; 203 put_u16(&resp.msg.data[1], sizepg); 204 put_u16(&resp.msg.data[3], sizepg); 205 resp.msg.response = MBOX_R_SUCCESS; 206 /* Wow that can't stay negated thats horrible */ 207 MSG_OUT("LPC_CTRL_IOCTL_MAP to 0x%08x for 0x%08x\n", map.addr, map.size); 208 r = ioctl(-context->fds[LPC_CTRL_FD].fd, 209 ASPEED_LPC_CTRL_IOCTL_MAP, &map); 210 if (r < 0) { 211 r = -errno; 212 resp.msg.response = MBOX_R_SYSTEM_ERROR; 213 MSG_ERR("Couldn't MAP ioctl(): %s\n", strerror(errno)); 214 } 215 break; 216 case MBOX_C_GET_FLASH_INFO: 217 put_u32(&resp.msg.data[0], context->mtd_info.size); 218 put_u32(&resp.msg.data[4], context->mtd_info.erasesize); 219 resp.msg.response = MBOX_R_SUCCESS; 220 break; 221 case MBOX_C_READ_WINDOW: 222 /* 223 * We could probably play tricks with LPC mapping. 224 * That would require kernel involvement. 225 * We could also always copy the relevant flash part to 226 * context->base even if it turns out that offset is in 227 * the window... 228 * This approach is easiest. 229 */ 230 if (context->dirty) { 231 r = read(-context->fds[MTD_FD].fd, context->lpc_mem, context->size); 232 if (r != context->size) { 233 MSG_ERR("Short read: %d expecting %"PRIu32"\n", r, context->size); 234 goto out; 235 } 236 } 237 basepg += get_u16(&req.msg.data[0]); 238 put_u16(&resp.msg.data[0], basepg); 239 resp.msg.response = MBOX_R_SUCCESS; 240 context->dirty = false; 241 break; 242 case MBOX_C_CLOSE_WINDOW: 243 context->dirty = true; 244 break; 245 case MBOX_C_WRITE_WINDOW: 246 basepg += get_u16(&req.msg.data[0]); 247 put_u16(&resp.msg.data[0], basepg); 248 resp.msg.response = MBOX_R_SUCCESS; 249 context->dirtybase = basepg << context->pgsize; 250 break; 251 /* Optimise these later */ 252 case MBOX_C_WRITE_DIRTY: 253 case MBOX_C_WRITE_FENCE: 254 dirtypg = get_u16(&req.msg.data[0]); 255 dirtycount = get_u32(&req.msg.data[2]); 256 if (dirtycount == 0) { 257 resp.msg.response = MBOX_R_PARAM_ERROR; 258 break; 259 } 260 /* 261 * dirtypg is actually offset within window so we probs 262 * need to know if the window isn't at zero 263 */ 264 if (flash_write(context, dirtypg << context->pgsize, dirtycount) != 0) { 265 resp.msg.response = MBOX_R_WRITE_ERROR; 266 break; 267 } 268 resp.msg.response = MBOX_R_SUCCESS; 269 break; 270 case MBOX_C_ACK: 271 resp.msg.response = MBOX_R_SUCCESS; 272 pos = lseek(context->fds[MBOX_FD].fd, MBOX_BMC_BYTE, SEEK_SET); 273 if (pos != MBOX_BMC_BYTE) { 274 r = -errno; 275 MSG_ERR("Couldn't lseek() to byte %d: %s\n", MBOX_BMC_BYTE, 276 strerror(errno)); 277 } 278 /* 279 * NAND what is in the hardware and the request. 280 * This prevents the host being able to SET bits, it can 281 * only request set ones be cleared. 282 */ 283 byte = ~(req.msg.data[0] & req.raw[MBOX_BMC_BYTE]); 284 len = write(context->fds[MBOX_FD].fd, &byte, 1); 285 if (len != 1) { 286 r = -errno; 287 MSG_ERR("Couldn't write to BMC status reg: %s\n", 288 strerror(errno)); 289 } 290 pos = lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET); 291 if (pos != 0) { 292 r = -errno; 293 MSG_ERR("Couldn't reset MBOX offset to zero\n"); 294 } 295 break; 296 case MBOX_C_COMPLETED_COMMANDS: 297 /* This implementation always completes before responding */ 298 resp.msg.data[0] = 0; 299 resp.msg.response = MBOX_R_SUCCESS; 300 break; 301 default: 302 MSG_ERR("UNKNOWN MBOX COMMAND\n"); 303 resp.msg.response = MBOX_R_PARAM_ERROR; 304 r = -1; 305 } 306 307 MSG_OUT("Writing response to MBOX regs\n"); 308 len = write(context->fds[MBOX_FD].fd, &resp, sizeof(resp.msg)); 309 if (len < sizeof(resp.msg)) { 310 r = -errno; 311 MSG_ERR("Didn't write the full response\n"); 312 } 313 314 out: 315 return r; 316 } 317 318 int copy_flash(struct mbox_context *context) 319 { 320 int r; 321 322 /* 323 * Copy flash into RAM early, same time. 324 * The kernel has created the LPC->AHB mapping also, which means 325 * flash should work. 326 * Ideally we tell the kernel whats up and when to do stuff... 327 */ 328 MSG_OUT("Loading flash into ram at %p for 0x%08x bytes\n", 329 context->lpc_mem, context->size); 330 if (lseek(context->fds[MTD_FD].fd, 0, SEEK_SET) != 0) { 331 r = -errno; 332 MSG_ERR("Couldn't reset MBOX pos to zero\n"); 333 return r; 334 } 335 r = read(context->fds[MTD_FD].fd, context->lpc_mem, context->size); 336 if (r != context->size) { 337 r = -errno; 338 MSG_ERR("Couldn't copy mtd into ram: %d\n", r); 339 return r; 340 } 341 return 0; 342 } 343 344 void signal_hup(int signum, siginfo_t *info, void *uc) 345 { 346 sighup = 1; 347 } 348 349 static void usage(const char *name) 350 { 351 fprintf(stderr, "Usage %s [ -v[v] | --syslog ]\n", name); 352 fprintf(stderr, "\t--verbose\t Be [more] verbose\n"); 353 fprintf(stderr, "\t--syslog\t Log output to syslog (pointless without -v)\n\n"); 354 } 355 356 int main(int argc, char *argv[]) 357 { 358 struct mbox_context *context; 359 const char *name = argv[0]; 360 char *pnor_filename = NULL; 361 int opt, polled, r, i; 362 struct aspeed_lpc_ctrl_mapping map; 363 struct sigaction act; 364 365 static const struct option long_options[] = { 366 { "verbose", no_argument, 0, 'v' }, 367 { "syslog", no_argument, 0, 's' }, 368 { 0, 0, 0, 0 } 369 }; 370 371 context = calloc(1, sizeof(*context)); 372 for (i = 0; i < TOTAL_FDS; i++) 373 context->fds[i].fd = -1; 374 375 mbox_vlog = &mbox_log_console; 376 while ((opt = getopt_long(argc, argv, "v", long_options, NULL)) != -1) { 377 switch (opt) { 378 case 0: 379 break; 380 case 'v': 381 verbosity++; 382 break; 383 case 's': 384 /* Avoid a double openlog() */ 385 if (mbox_vlog != &vsyslog) { 386 openlog(PREFIX, LOG_ODELAY, LOG_DAEMON); 387 mbox_vlog = &vsyslog; 388 } 389 break; 390 default: 391 usage(name); 392 exit(EXIT_FAILURE); 393 } 394 } 395 396 if (verbosity == MBOX_LOG_VERBOSE) 397 MSG_OUT("Verbose logging\n"); 398 399 if (verbosity == MBOX_LOG_DEBUG) 400 MSG_OUT("Debug logging\n"); 401 402 MSG_OUT("Registering SigHUP hander\n"); 403 act.sa_sigaction = signal_hup; 404 sigemptyset(&act.sa_mask); 405 act.sa_flags = SA_SIGINFO; 406 if (sigaction(SIGHUP, &act, NULL) < 0) { 407 perror("Registering SIGHUP"); 408 exit(1); 409 } 410 sighup = 0; 411 412 MSG_OUT("Starting\n"); 413 414 MSG_OUT("Opening %s\n", MBOX_HOST_PATH); 415 context->fds[MBOX_FD].fd = open(MBOX_HOST_PATH, O_RDWR | O_NONBLOCK); 416 if (context->fds[MBOX_FD].fd < 0) { 417 r = -errno; 418 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 419 MBOX_HOST_PATH, strerror(errno)); 420 goto finish; 421 } 422 423 MSG_OUT("Opening %s\n", LPC_CTRL_PATH); 424 context->fds[LPC_CTRL_FD].fd = open(LPC_CTRL_PATH, O_RDWR | O_SYNC); 425 if (context->fds[LPC_CTRL_FD].fd < 0) { 426 r = -errno; 427 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 428 LPC_CTRL_PATH, strerror(errno)); 429 goto finish; 430 } 431 432 MSG_OUT("Getting buffer size...\n"); 433 /* This may become more variable in the future */ 434 context->pgsize = 12; /* 4K */ 435 map.window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY; 436 map.window_id = 0; /* Theres only one */ 437 if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, 438 &map) < 0) { 439 r = -errno; 440 MSG_OUT("fail\n"); 441 MSG_ERR("Couldn't get lpc control buffer size: %s\n", strerror(-r)); 442 goto finish; 443 } 444 /* And strip the first nibble, LPC access speciality */ 445 context->size = map.size; 446 context->base = -context->size & 0x0FFFFFFF; 447 448 /* READ THE COMMENT AT THE START OF THIS FUNCTION! */ 449 r = point_to_flash(context); 450 if (r) { 451 MSG_ERR("Failed to point the LPC BUS at the actual flash: %s\n", 452 strerror(-r)); 453 goto finish; 454 } 455 456 MSG_OUT("Mapping %s for %u\n", LPC_CTRL_PATH, context->size); 457 context->lpc_mem = mmap(NULL, context->size, PROT_READ | PROT_WRITE, MAP_SHARED, 458 context->fds[LPC_CTRL_FD].fd, 0); 459 if (context->lpc_mem == MAP_FAILED) { 460 r = -errno; 461 MSG_ERR("Didn't manage to mmap %s: %s\n", LPC_CTRL_PATH, strerror(errno)); 462 goto finish; 463 } 464 465 pnor_filename = get_dev_mtd(); 466 if (!pnor_filename) { 467 MSG_ERR("Couldn't find the PNOR /dev/mtd partition\n"); 468 r = -1; 469 goto finish; 470 } 471 472 MSG_OUT("Opening %s\n", pnor_filename); 473 context->fds[MTD_FD].fd = open(pnor_filename, O_RDWR); 474 if (context->fds[MTD_FD].fd < 0) { 475 r = -errno; 476 MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", 477 pnor_filename, strerror(errno)); 478 goto finish; 479 } 480 481 if (ioctl(context->fds[MTD_FD].fd, MEMGETINFO, &context->mtd_info) == -1) { 482 MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno)); 483 return -1; 484 } 485 486 if (copy_flash(context)) 487 goto finish; 488 489 context->fds[MBOX_FD].events = POLLIN; 490 /* Ignore in poll() */ 491 context->fds[LPC_CTRL_FD].fd = -context->fds[LPC_CTRL_FD].fd; 492 context->fds[MTD_FD].fd = -context->fds[MTD_FD].fd; 493 494 /* Test the single write facility by setting all the regs to 0xFF */ 495 MSG_OUT("Setting all MBOX regs to 0xff individually...\n"); 496 for (i = 0; i < MBOX_REG_BYTES; i++) { 497 uint8_t byte = 0xff; 498 off_t pos; 499 int len; 500 501 pos = lseek(context->fds[MBOX_FD].fd, i, SEEK_SET); 502 if (pos != i) { 503 MSG_ERR("Couldn't lseek() to byte %d: %s\n", i, 504 strerror(errno)); 505 break; 506 } 507 len = write(context->fds[MBOX_FD].fd, &byte, 1); 508 if (len != 1) { 509 MSG_ERR("Couldn't write MBOX reg %d: %s\n", i, 510 strerror(errno)); 511 break; 512 } 513 } 514 if (lseek(context->fds[MBOX_FD].fd, 0, SEEK_SET) != 0) { 515 r = -errno; 516 MSG_ERR("Couldn't reset MBOX pos to zero\n"); 517 goto finish; 518 } 519 520 MSG_OUT("Entering polling loop\n"); 521 while (running) { 522 polled = poll(context->fds, TOTAL_FDS, 1000); 523 if (polled == 0) 524 continue; 525 if ((polled == -1) && (errno != -EINTR) && (sighup == 1)) { 526 /* Got sighup. reset to point to flash and 527 * reread flash */ 528 context->fds[LPC_CTRL_FD].fd = -context->fds[LPC_CTRL_FD].fd; 529 r = point_to_flash(context); 530 if (r) { 531 goto finish; 532 } 533 context->fds[LPC_CTRL_FD].fd = -context->fds[LPC_CTRL_FD].fd; 534 context->fds[MTD_FD].fd = -context->fds[MTD_FD].fd; 535 r = copy_flash(context); 536 if (r) 537 goto finish; 538 context->fds[MTD_FD].fd = -context->fds[MTD_FD].fd; 539 sighup = 0; 540 continue; 541 } 542 if (polled < 0) { 543 r = -errno; 544 MSG_ERR("Error from poll(): %s\n", strerror(errno)); 545 break; 546 } 547 r = dispatch_mbox(context); 548 if (r < 0) { 549 MSG_ERR("Error handling MBOX event: %s\n", strerror(-r)); 550 break; 551 } 552 } 553 554 MSG_OUT("Exiting\n"); 555 556 /* Unnegate so we can close it */ 557 context->fds[LPC_CTRL_FD].fd = -context->fds[LPC_CTRL_FD].fd; 558 context->fds[MTD_FD].fd = -context->fds[MTD_FD].fd; 559 560 finish: 561 if (context->lpc_mem) 562 munmap(context->lpc_mem, context->size); 563 564 free(pnor_filename); 565 close(context->fds[MTD_FD].fd); 566 close(context->fds[LPC_CTRL_FD].fd); 567 close(context->fds[MBOX_FD].fd); 568 free(context); 569 570 return r; 571 } 572 573