示例#1
2
static const struct baudentry *round_baud(unsigned int baud)
{
	int i;
	/* Round baud rate to next lower entry in sp_baudtable if it exists, else use the lowest entry. */
	for (i = ARRAY_SIZE(sp_baudtable) - 2; i >= 0 ; i--) {
		if (sp_baudtable[i].baud == baud)
			return &sp_baudtable[i];

		if (sp_baudtable[i].baud < baud) {
			msg_pwarn("Warning: given baudrate %d rounded down to %d.\n",
				  baud, sp_baudtable[i].baud);
			return &sp_baudtable[i];
		}
	}
	msg_pinfo("Using slowest possible baudrate: %d.\n", sp_baudtable[0].baud);
	return &sp_baudtable[0];
}
示例#2
0
int gfxnvidia_init(void)
{
	uint32_t reg32;

	if (rget_io_perms())
		return 1;

	io_base_addr = pcidev_init(PCI_BASE_ADDRESS_0, gfx_nvidia);

	io_base_addr += 0x300000;
	msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);

	nvidia_bar = physmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE);

	/* Must be done before rpci calls. */
	if (register_shutdown(gfxnvidia_shutdown, NULL))
		return 1;

	/* Allow access to flash interface (will disable screen). */
	reg32 = pci_read_long(pcidev_dev, 0x50);
	reg32 &= ~(1 << 0);
	rpci_write_long(pcidev_dev, 0x50, reg32);

	/* Write/erase doesn't work. */
	programmer_may_write = 0;
	register_par_programmer(&par_programmer_gfxnvidia, BUS_PARALLEL);

	return 0;
}
示例#3
0
int gfxnvidia_init(void)
{
	uint32_t reg32;

	get_io_perms();

	io_base_addr = pcidev_init(PCI_VENDOR_ID_NVIDIA, PCI_BASE_ADDRESS_0,
				   gfx_nvidia);

	io_base_addr += 0x300000;
	msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);

	/* Allow access to flash interface (will disable screen). */
	reg32 = pci_read_long(pcidev_dev, 0x50);
	reg32 &= ~(1 << 0);
	rpci_write_long(pcidev_dev, 0x50, reg32);

	nvidia_bar = physmap("NVIDIA", io_base_addr, 16 * 1024 * 1024);

	buses_supported = CHIP_BUSTYPE_PARALLEL;

	/* Write/erase doesn't work. */
	programmer_may_write = 0;

	return 0;
}
示例#4
0
/* Check the number of bytes to be transmitted and extract opcode. */
static int check_readwritecnt(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt)
{
	unsigned int maxwritecnt = flash->mst->spi.max_data_write + 3;
	if (writecnt > maxwritecnt) {
		msg_pinfo("%s: SPI controller can not send %d bytes, it is limited to %d bytes\n",
			  __func__, writecnt, maxwritecnt);
		return SPI_INVALID_LENGTH;
	}

	unsigned int maxreadcnt = flash->mst->spi.max_data_read;
	if (readcnt > maxreadcnt) {
		msg_pinfo("%s: SPI controller can not receive %d bytes, it is limited to %d bytes\n",
			  __func__, readcnt, maxreadcnt);
		return SPI_INVALID_LENGTH;
	}
	return 0;
}
示例#5
0
int setup_cpu_msr(int cpu)
{
	char msrfilename[64];
	memset(msrfilename, 0, sizeof(msrfilename));
	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu/%d/msr", cpu);

	if (fd_msr != -1) {
		msg_pinfo("MSR was already initialized\n");
		return -1;
	}

	fd_msr = open(msrfilename, O_RDWR);

	if (fd_msr < 0) {
		perror("Error while opening /dev/cpu/0/msr");
		msg_pinfo("Did you run 'modprobe msr'?\n");
		return -1;
	}

	return 0;
}
示例#6
0
int setup_cpu_msr(int cpu)
{
	char msrfilename[64];
	memset(msrfilename, 0, sizeof(msrfilename));
	snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu%d", cpu);

	if (fd_msr != -1) {
		msg_pinfo("MSR was already initialized\n");
		return -1;
	}

	fd_msr = open(msrfilename, O_RDWR);

	if (fd_msr < 0) {
		perror("Error while opening /dev/cpu0");
		msg_pinfo("Did you install ports/sysutils/devcpu?\n");
		return -1;
	}

	return 0;
}
示例#7
0
void cleanup_cpu_msr(void)
{
	if (fd_msr == -1) {
		msg_pinfo("No MSR initialized.\n");
		return;
	}

	close(fd_msr);

	/* Clear MSR file descriptor */
	fd_msr = -1;
}
示例#8
0
int setup_cpu_msr(int cpu)
{
	char msrfilename[64];
	memset(msrfilename, 0, sizeof(msrfilename));
	snprintf(msrfilename, sizeof(msrfilename), "/dev/amdmsr");

	if (fd_msr != -1) {
		msg_pinfo("MSR was already initialized\n");
		return -1;
	}

	fd_msr = open(msrfilename, O_RDWR);

	if (fd_msr < 0) {
		msg_perr("Error while opening %s: %s\n", msrfilename, strerror(errno));
		return -1;
	}

	return 0;
}
示例#9
0
/* Returns 0 upon success, a negative number upon errors. */
static int mstarddc_spi_shutdown(void *data)
{
	// Reset, disables ISP mode
	if (mstarddc_doreset == 1) {
		uint8_t cmd = MSTARDDC_SPI_RESET;
		if (write(mstarddc_fd, &cmd, 1) < 0) {
			msg_perr("Error sending reset command: errno %d.\n",
				 errno);
			return -1;
		}
	} else {
		msg_pinfo("Info: Reset command was not sent. "
			  "Either the noreset=1 option was used, "
			  "or an error occured.\n");
	}

	if (close(mstarddc_fd) < 0) {
		msg_perr("Error closing device: errno %d.\n", errno);
		return -1;
	}
	return 0;
}
示例#10
0
/*
 * The IT8716F only supports commands with length 1,2,4,5 bytes including
 * command byte and can not read more than 3 bytes from the device.
 *
 * This function expects writearr[0] to be the first byte sent to the device,
 * whereas the IT8716F splits commands internally into address and non-address
 * commands with the address in inverse wire order. That's why the register
 * ordering in case 4 and 5 may seem strange.
 */
static int it8716f_spi_send_command(unsigned int writecnt, unsigned int readcnt,
			const unsigned char *writearr, unsigned char *readarr)
{
	uint8_t busy, writeenc;
	int i;

	do {
		busy = INB(it8716f_flashport) & 0x80;
	} while (busy);
	if (readcnt > 3) {
		msg_pinfo("%s called with unsupported readcnt %i.\n",
		       __func__, readcnt);
		return SPI_INVALID_LENGTH;
	}
	switch (writecnt) {
	case 1:
		OUTB(writearr[0], it8716f_flashport + 1);
		writeenc = 0x0;
		break;
	case 2:
		OUTB(writearr[0], it8716f_flashport + 1);
		OUTB(writearr[1], it8716f_flashport + 7);
		writeenc = 0x1;
		break;
	case 4:
		OUTB(writearr[0], it8716f_flashport + 1);
		OUTB(writearr[1], it8716f_flashport + 4);
		OUTB(writearr[2], it8716f_flashport + 3);
		OUTB(writearr[3], it8716f_flashport + 2);
		writeenc = 0x2;
		break;
	case 5:
		OUTB(writearr[0], it8716f_flashport + 1);
		OUTB(writearr[1], it8716f_flashport + 4);
		OUTB(writearr[2], it8716f_flashport + 3);
		OUTB(writearr[3], it8716f_flashport + 2);
		OUTB(writearr[4], it8716f_flashport + 7);
		writeenc = 0x3;
		break;
	default:
		msg_pinfo("%s called with unsupported writecnt %i.\n",
		       __func__, writecnt);
		return SPI_INVALID_LENGTH;
	}
	/*
	 * Start IO, 33 or 16 MHz, readcnt input bytes, writecnt output bytes.
	 * Note:
	 * We can't use writecnt directly, but have to use a strange encoding.
	 */
	OUTB(((0x4 + (fast_spi ? 1 : 0)) << 4)
		| ((readcnt & 0x3) << 2) | (writeenc), it8716f_flashport);

	if (readcnt > 0) {
		do {
			busy = INB(it8716f_flashport) & 0x80;
		} while (busy);

		for (i = 0; i < readcnt; i++)
			readarr[i] = INB(it8716f_flashport + 5 + i);
	}

	return 0;
}
示例#11
0
int main(int argc, char *argv[])
{
	const struct flashchip *chip = NULL;
	/* Probe for up to eight flash chips. */
	struct flashctx flashes[8] = {{0}};
	struct flashctx *fill_flash;
	const char *name;
	int namelen, opt, i, j;
	int startchip = -1, chipcount = 0, option_index = 0, force = 0, ifd = 0, fmap = 0;
#if CONFIG_PRINT_WIKI == 1
	int list_supported_wiki = 0;
#endif
	int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
	int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0;
	struct flashrom_layout *layout = NULL;
	enum programmer prog = PROGRAMMER_INVALID;
	enum {
		OPTION_IFD = 0x0100,
		OPTION_FMAP,
		OPTION_FMAP_FILE,
		OPTION_FLASH_CONTENTS,
	};
	int ret = 0;

	static const char optstring[] = "r:Rw:v:nNVEfc:l:i:p:Lzho:";
	static const struct option long_options[] = {
		{"read",		1, NULL, 'r'},
		{"write",		1, NULL, 'w'},
		{"erase",		0, NULL, 'E'},
		{"verify",		1, NULL, 'v'},
		{"noverify",		0, NULL, 'n'},
		{"noverify-all",	0, NULL, 'N'},
		{"chip",		1, NULL, 'c'},
		{"verbose",		0, NULL, 'V'},
		{"force",		0, NULL, 'f'},
		{"layout",		1, NULL, 'l'},
		{"ifd",			0, NULL, OPTION_IFD},
		{"fmap",		0, NULL, OPTION_FMAP},
		{"fmap-file",		1, NULL, OPTION_FMAP_FILE},
		{"image",		1, NULL, 'i'},
		{"flash-contents",	1, NULL, OPTION_FLASH_CONTENTS},
		{"list-supported",	0, NULL, 'L'},
		{"list-supported-wiki",	0, NULL, 'z'},
		{"programmer",		1, NULL, 'p'},
		{"help",		0, NULL, 'h'},
		{"version",		0, NULL, 'R'},
		{"output",		1, NULL, 'o'},
		{NULL,			0, NULL, 0},
	};

	char *filename = NULL;
	char *referencefile = NULL;
	char *layoutfile = NULL;
	char *fmapfile = NULL;
#ifndef STANDALONE
	char *logfile = NULL;
#endif /* !STANDALONE */
	char *tempstr = NULL;
	char *pparam = NULL;

	flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);

	print_version();
	print_banner();

	if (selfcheck())
		exit(1);

	setbuf(stdout, NULL);
	/* FIXME: Delay all operation_specified checks until after command
	 * line parsing to allow --help overriding everything else.
	 */
	while ((opt = getopt_long(argc, argv, optstring,
				  long_options, &option_index)) != EOF) {
		switch (opt) {
		case 'r':
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			filename = strdup(optarg);
			read_it = 1;
			break;
		case 'w':
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			filename = strdup(optarg);
			write_it = 1;
			break;
		case 'v':
			//FIXME: gracefully handle superfluous -v
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (dont_verify_it) {
				fprintf(stderr, "--verify and --noverify are mutually exclusive. Aborting.\n");
				cli_classic_abort_usage();
			}
			filename = strdup(optarg);
			verify_it = 1;
			break;
		case 'n':
			if (verify_it) {
				fprintf(stderr, "--verify and --noverify are mutually exclusive. Aborting.\n");
				cli_classic_abort_usage();
			}
			dont_verify_it = 1;
			break;
		case 'N':
			dont_verify_all = 1;
			break;
		case 'c':
			chip_to_probe = strdup(optarg);
			break;
		case 'V':
			verbose_screen++;
			if (verbose_screen > FLASHROM_MSG_DEBUG2)
				verbose_logfile = verbose_screen;
			break;
		case 'E':
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			erase_it = 1;
			break;
		case 'f':
			force = 1;
			break;
		case 'l':
			if (layoutfile) {
				fprintf(stderr, "Error: --layout specified "
					"more than once. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (ifd) {
				fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (fmap) {
				fprintf(stderr, "Error: --layout and --fmap-file both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			layoutfile = strdup(optarg);
			break;
		case OPTION_IFD:
			if (layoutfile) {
				fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (fmap) {
				fprintf(stderr, "Error: --fmap-file and --ifd both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			ifd = 1;
			break;
		case OPTION_FMAP_FILE:
			if (fmap) {
				fprintf(stderr, "Error: --fmap or --fmap-file specified "
					"more than once. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (ifd) {
				fprintf(stderr, "Error: --fmap-file and --ifd both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (layoutfile) {
				fprintf(stderr, "Error: --fmap-file and --layout both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			fmapfile = strdup(optarg);
			fmap = 1;
			break;
		case OPTION_FMAP:
			if (fmap) {
				fprintf(stderr, "Error: --fmap or --fmap-file specified "
					"more than once. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (ifd) {
				fprintf(stderr, "Error: --fmap and --ifd both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			if (layoutfile) {
				fprintf(stderr, "Error: --layout and --fmap both specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			fmap = 1;
			break;
		case 'i':
			tempstr = strdup(optarg);
			if (register_include_arg(tempstr)) {
				free(tempstr);
				cli_classic_abort_usage();
			}
			break;
		case OPTION_FLASH_CONTENTS:
			referencefile = strdup(optarg);
			break;
		case 'L':
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			list_supported = 1;
			break;
		case 'z':
#if CONFIG_PRINT_WIKI == 1
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			list_supported_wiki = 1;
#else
			fprintf(stderr, "Error: Wiki output was not compiled "
				"in. Aborting.\n");
			cli_classic_abort_usage();
#endif
			break;
		case 'p':
			if (prog != PROGRAMMER_INVALID) {
				fprintf(stderr, "Error: --programmer specified "
					"more than once. You can separate "
					"multiple\nparameters for a programmer "
					"with \",\". Please see the man page "
					"for details.\n");
				cli_classic_abort_usage();
			}
			for (prog = 0; prog < PROGRAMMER_INVALID; prog++) {
				name = programmer_table[prog].name;
				namelen = strlen(name);
				if (strncmp(optarg, name, namelen) == 0) {
					switch (optarg[namelen]) {
					case ':':
						pparam = strdup(optarg + namelen + 1);
						if (!strlen(pparam)) {
							free(pparam);
							pparam = NULL;
						}
						break;
					case '\0':
						break;
					default:
						/* The continue refers to the
						 * for loop. It is here to be
						 * able to differentiate between
						 * foo and foobar.
						 */
						continue;
					}
					break;
				}
			}
			if (prog == PROGRAMMER_INVALID) {
				fprintf(stderr, "Error: Unknown programmer \"%s\". Valid choices are:\n",
					optarg);
				list_programmers_linebreak(0, 80, 0);
				msg_ginfo(".\n");
				cli_classic_abort_usage();
			}
			break;
		case 'R':
			/* print_version() is always called during startup. */
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			exit(0);
			break;
		case 'h':
			if (++operation_specified > 1) {
				fprintf(stderr, "More than one operation "
					"specified. Aborting.\n");
				cli_classic_abort_usage();
			}
			cli_classic_usage(argv[0]);
			exit(0);
			break;
		case 'o':
#ifdef STANDALONE
			fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n");
			cli_classic_abort_usage();
#else /* STANDALONE */
			logfile = strdup(optarg);
			if (logfile[0] == '\0') {
				fprintf(stderr, "No log filename specified.\n");
				cli_classic_abort_usage();
			}
#endif /* STANDALONE */
			break;
		default:
			cli_classic_abort_usage();
			break;
		}
	}

	if (optind < argc) {
		fprintf(stderr, "Error: Extra parameter found.\n");
		cli_classic_abort_usage();
	}

	if ((read_it | write_it | verify_it) && check_filename(filename, "image")) {
		cli_classic_abort_usage();
	}
	if (layoutfile && check_filename(layoutfile, "layout")) {
		cli_classic_abort_usage();
	}
	if (fmapfile && check_filename(fmapfile, "fmap")) {
		cli_classic_abort_usage();
	}
	if (referencefile && check_filename(referencefile, "reference")) {
		cli_classic_abort_usage();
	}

#ifndef STANDALONE
	if (logfile && check_filename(logfile, "log"))
		cli_classic_abort_usage();
	if (logfile && open_logfile(logfile))
		cli_classic_abort_usage();
#endif /* !STANDALONE */

#if CONFIG_PRINT_WIKI == 1
	if (list_supported_wiki) {
		print_supported_wiki();
		goto out;
	}
#endif

	if (list_supported) {
		if (print_supported())
			ret = 1;
		goto out;
	}

#ifndef STANDALONE
	start_logging();
#endif /* !STANDALONE */

	print_buildinfo();
	msg_gdbg("Command line (%i args):", argc - 1);
	for (i = 0; i < argc; i++) {
		msg_gdbg(" %s", argv[i]);
	}
	msg_gdbg("\n");

	if (layoutfile && read_romlayout(layoutfile)) {
		ret = 1;
		goto out;
	}

	if (!ifd && !fmap && process_include_args(get_global_layout())) {
		ret = 1;
		goto out;
	}
	/* Does a chip with the requested name exist in the flashchips array? */
	if (chip_to_probe) {
		for (chip = flashchips; chip && chip->name; chip++)
			if (!strcmp(chip->name, chip_to_probe))
				break;
		if (!chip || !chip->name) {
			msg_cerr("Error: Unknown chip '%s' specified.\n", chip_to_probe);
			msg_gerr("Run flashrom -L to view the hardware supported in this flashrom version.\n");
			ret = 1;
			goto out;
		}
		/* Keep chip around for later usage in case a forced read is requested. */
	}

	if (prog == PROGRAMMER_INVALID) {
		if (CONFIG_DEFAULT_PROGRAMMER != PROGRAMMER_INVALID) {
			prog = CONFIG_DEFAULT_PROGRAMMER;
			/* We need to strdup here because we free(pparam) unconditionally later. */
			pparam = strdup(CONFIG_DEFAULT_PROGRAMMER_ARGS);
			msg_pinfo("Using default programmer \"%s\" with arguments \"%s\".\n",
				  programmer_table[CONFIG_DEFAULT_PROGRAMMER].name, pparam);
		} else {
			msg_perr("Please select a programmer with the --programmer parameter.\n"
				 "Previously this was not necessary because there was a default set.\n"
#if CONFIG_INTERNAL == 1
				 "To choose the mainboard of this computer use 'internal'. "
#endif
				 "Valid choices are:\n");
			list_programmers_linebreak(0, 80, 0);
			msg_ginfo(".\n");
			ret = 1;
			goto out;
		}
	}

	/* FIXME: Delay calibration should happen in programmer code. */
	myusec_calibrate_delay();

	if (programmer_init(prog, pparam)) {
		msg_perr("Error: Programmer initialization failed.\n");
		ret = 1;
		goto out_shutdown;
	}
	tempstr = flashbuses_to_text(get_buses_supported());
	msg_pdbg("The following protocols are supported: %s.\n", tempstr);
	free(tempstr);

	for (j = 0; j < registered_master_count; j++) {
		startchip = 0;
		while (chipcount < ARRAY_SIZE(flashes)) {
			startchip = probe_flash(&registered_masters[j], startchip, &flashes[chipcount], 0);
			if (startchip == -1)
				break;
			chipcount++;
			startchip++;
		}
	}

	if (chipcount > 1) {
		msg_cinfo("Multiple flash chip definitions match the detected chip(s): \"%s\"",
			  flashes[0].chip->name);
		for (i = 1; i < chipcount; i++)
			msg_cinfo(", \"%s\"", flashes[i].chip->name);
		msg_cinfo("\nPlease specify which chip definition to use with the -c <chipname> option.\n");
		ret = 1;
		goto out_shutdown;
	} else if (!chipcount) {
		msg_cinfo("No EEPROM/flash device found.\n");
		if (!force || !chip_to_probe) {
			msg_cinfo("Note: flashrom can never write if the flash chip isn't found "
				  "automatically.\n");
		}
		if (force && read_it && chip_to_probe) {
			struct registered_master *mst;
			int compatible_masters = 0;
			msg_cinfo("Force read (-f -r -c) requested, pretending the chip is there:\n");
			/* This loop just counts compatible controllers. */
			for (j = 0; j < registered_master_count; j++) {
				mst = &registered_masters[j];
				/* chip is still set from the chip_to_probe earlier in this function. */
				if (mst->buses_supported & chip->bustype)
					compatible_masters++;
			}
			if (!compatible_masters) {
				msg_cinfo("No compatible controller found for the requested flash chip.\n");
				ret = 1;
				goto out_shutdown;
			}
			if (compatible_masters > 1)
				msg_cinfo("More than one compatible controller found for the requested flash "
					  "chip, using the first one.\n");
			for (j = 0; j < registered_master_count; j++) {
				mst = &registered_masters[j];
				startchip = probe_flash(mst, 0, &flashes[0], 1);
				if (startchip != -1)
					break;
			}
			if (startchip == -1) {
				// FIXME: This should never happen! Ask for a bug report?
				msg_cinfo("Probing for flash chip '%s' failed.\n", chip_to_probe);
				ret = 1;
				goto out_shutdown;
			}
			if (map_flash(&flashes[0]) != 0) {
				free(flashes[0].chip);
				ret = 1;
				goto out_shutdown;
			}
			msg_cinfo("Please note that forced reads most likely contain garbage.\n");
			ret = read_flash_to_file(&flashes[0], filename);
			unmap_flash(&flashes[0]);
			free(flashes[0].chip);
			goto out_shutdown;
		}
		ret = 1;
		goto out_shutdown;
	} else if (!chip_to_probe) {
		/* repeat for convenience when looking at foreign logs */
		tempstr = flashbuses_to_text(flashes[0].chip->bustype);
		msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n",
			 flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr);
		free(tempstr);
	}

	fill_flash = &flashes[0];

	print_chip_support_status(fill_flash->chip);

	unsigned int limitexceeded = count_max_decode_exceedings(fill_flash);
	if (limitexceeded > 0 && !force) {
		enum chipbustype commonbuses = fill_flash->mst->buses_supported & fill_flash->chip->bustype;

		/* Sometimes chip and programmer have more than one bus in common,
		 * and the limit is not exceeded on all buses. Tell the user. */
		if ((bitcount(commonbuses) > limitexceeded)) {
			msg_pdbg("There is at least one interface available which could support the size of\n"
				 "the selected flash chip.\n");
		}
		msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
			 "Use --force/-f to override at your own risk.\n");
		ret = 1;
		goto out_shutdown;
	}

	if (!(read_it | write_it | verify_it | erase_it)) {
		msg_ginfo("No operations were specified.\n");
		goto out_shutdown;
	}

	if (layoutfile) {
		layout = get_global_layout();
	} else if (ifd && (flashrom_layout_read_from_ifd(&layout, fill_flash, NULL, 0) ||
			   process_include_args(layout))) {
		ret = 1;
		goto out_shutdown;
	} else if (fmap && fmapfile) {
		struct stat s;
		if (stat(fmapfile, &s) != 0) {
			msg_gerr("Failed to stat fmapfile \"%s\"\n", fmapfile);
			ret = 1;
			goto out_shutdown;
		}

		size_t fmapfile_size = s.st_size;
		uint8_t *fmapfile_buffer = malloc(fmapfile_size);
		if (!fmapfile_buffer) {
			ret = 1;
			goto out_shutdown;
		}

		if (read_buf_from_file(fmapfile_buffer, fmapfile_size, fmapfile)) {
			ret = 1;
			free(fmapfile_buffer);
			goto out_shutdown;
		}

		if (flashrom_layout_read_fmap_from_buffer(&layout, fill_flash, fmapfile_buffer, fmapfile_size) ||
				process_include_args(layout)) {
			ret = 1;
			free(fmapfile_buffer);
			goto out_shutdown;
		}
		free(fmapfile_buffer);
	} else if (fmap && (flashrom_layout_read_fmap_from_rom(&layout, fill_flash, 0,
				fill_flash->chip->total_size * 1024) || process_include_args(layout))) {
		ret = 1;
		goto out_shutdown;
	}

	flashrom_layout_set(fill_flash, layout);
	flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE, !!force);
#if CONFIG_INTERNAL == 1
	flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE_BOARDMISMATCH, !!force_boardmismatch);
#endif
	flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_AFTER_WRITE, !dont_verify_it);
	flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, !dont_verify_all);

	/* FIXME: We should issue an unconditional chip reset here. This can be
	 * done once we have a .reset function in struct flashchip.
	 * Give the chip time to settle.
	 */
	programmer_delay(100000);
	if (read_it)
		ret = do_read(fill_flash, filename);
	else if (erase_it)
		ret = do_erase(fill_flash);
	else if (write_it)
		ret = do_write(fill_flash, filename, referencefile);
	else if (verify_it)
		ret = do_verify(fill_flash, filename);

	flashrom_layout_release(layout);

out_shutdown:
	programmer_shutdown();
out:
	for (i = 0; i < chipcount; i++)
		free(flashes[i].chip);

	layout_cleanup();
	free(filename);
	free(fmapfile);
	free(referencefile);
	free(layoutfile);
	free(pparam);
	/* clean up global variables */
	free((char *)chip_to_probe); /* Silence! Freeing is not modifying contents. */
	chip_to_probe = NULL;
#ifndef STANDALONE
	free(logfile);
	ret |= close_logfile();
#endif /* !STANDALONE */
	return ret;
}
示例#12
0
int setup_cpu_msr(int cpu)
{
	msg_pinfo("No MSR support for your OS yet.\n");
	return -1;
}
示例#13
0
文件: pcidev.c 项目: 173210/flashrom
/* pcidev_init gets an array of allowed PCI device IDs and returns a pointer to struct pci_dev iff exactly one
 * match was found. If the "pci=bb:dd.f" programmer parameter was specified, a match is only considered if it
 * also matches the specified bus:device.function.
 * For convenience, this function also registers its own undo handlers.
 */
struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar)
{
	struct pci_dev *dev;
	struct pci_dev *found_dev = NULL;
	struct pci_filter filter;
	char *pcidev_bdf;
	char *msg = NULL;
	int found = 0;
	int i;
	uintptr_t addr = 0;

	if (pci_init_common() != 0)
		return NULL;
	pci_filter_init(pacc, &filter);

	/* Filter by bb:dd.f (if supplied by the user). */
	pcidev_bdf = extract_programmer_param("pci");
	if (pcidev_bdf != NULL) {
		if ((msg = pci_filter_parse_slot(&filter, pcidev_bdf))) {
			msg_perr("Error: %s\n", msg);
			return NULL;
		}
	}
	free(pcidev_bdf);

	for (dev = pacc->devices; dev; dev = dev->next) {
		if (pci_filter_match(&filter, dev)) {
			/* Check against list of supported devices. */
			for (i = 0; devs[i].device_name != NULL; i++)
				if ((dev->vendor_id == devs[i].vendor_id) &&
				    (dev->device_id == devs[i].device_id))
					break;
			/* Not supported, try the next one. */
			if (devs[i].device_name == NULL)
				continue;

			msg_pdbg("Found \"%s %s\" (%04x:%04x, BDF %02x:%02x.%x).\n", devs[i].vendor_name,
				 devs[i].device_name, dev->vendor_id, dev->device_id, dev->bus, dev->dev,
				 dev->func);
			if (devs[i].status == NT)
				msg_pinfo("===\nThis PCI device is UNTESTED. Please report the 'flashrom -p "
					  "xxxx' output \n"
					  "to [email protected] if it works for you. Please add the name "
					  "of your\n"
					  "PCI device to the subject. Thank you for your help!\n===\n");

			/* FIXME: We should count all matching devices, not
			 * just those with a valid BAR.
			 */
			if ((addr = pcidev_readbar(dev, bar)) != 0) {
				found_dev = dev;
				found++;
			}
		}
	}

	/* Only continue if exactly one supported PCI dev has been found. */
	if (found == 0) {
		msg_perr("Error: No supported PCI device found.\n");
		return NULL;
	} else if (found > 1) {
		msg_perr("Error: Multiple supported PCI devices found. Use 'flashrom -p xxxx:pci=bb:dd.f' \n"
			 "to explicitly select the card with the given BDF (PCI bus, device, function).\n");
		return NULL;
	}

	return found_dev;
}
示例#14
0
int internal_init(void)
{
#if __FLASHROM_LITTLE_ENDIAN__
	int ret = 0;
#endif
	int force_laptop = 0;
	int not_a_laptop = 0;
	const char *board_vendor = NULL;
	const char *board_model = NULL;
#if defined (__i386__) || defined (__x86_64__) || defined (__arm__)
	const char *cb_vendor = NULL;
	const char *cb_model = NULL;
#endif
	char *arg;

	arg = extract_programmer_param("boardenable");
	if (arg && !strcmp(arg,"force")) {
		force_boardenable = 1;
	} else if (arg && !strlen(arg)) {
		msg_perr("Missing argument for boardenable.\n");
		free(arg);
		return 1;
	} else if (arg) {
		msg_perr("Unknown argument for boardenable: %s\n", arg);
		free(arg);
		return 1;
	}
	free(arg);

	arg = extract_programmer_param("boardmismatch");
	if (arg && !strcmp(arg,"force")) {
		force_boardmismatch = 1;
	} else if (arg && !strlen(arg)) {
		msg_perr("Missing argument for boardmismatch.\n");
		free(arg);
		return 1;
	} else if (arg) {
		msg_perr("Unknown argument for boardmismatch: %s\n", arg);
		free(arg);
		return 1;
	}
	free(arg);

	arg = extract_programmer_param("laptop");
	if (arg && !strcmp(arg, "force_I_want_a_brick"))
		force_laptop = 1;
	else if (arg && !strcmp(arg, "this_is_not_a_laptop"))
		not_a_laptop = 1;
	else if (arg && !strlen(arg)) {
		msg_perr("Missing argument for laptop.\n");
		free(arg);
		return 1;
	} else if (arg) {
		msg_perr("Unknown argument for laptop: %s\n", arg);
		free(arg);
		return 1;
	}
	free(arg);

	arg = extract_programmer_param("mainboard");
	if (arg && strlen(arg)) {
		if (board_parse_parameter(arg, &board_vendor, &board_model)) {
			free(arg);
			return 1;
		}
	} else if (arg && !strlen(arg)) {
		msg_perr("Missing argument for mainboard.\n");
		free(arg);
		return 1;
	}
	free(arg);

	if (rget_io_perms())
		return 1;
	if (register_shutdown(internal_shutdown, NULL))
		return 1;

	/* Default to Parallel/LPC/FWH flash devices. If a known host controller
	 * is found, the host controller init routine sets the
	 * internal_buses_supported bitfield.
	 */
	internal_buses_supported = BUS_NONSPI;

	/* Initialize PCI access for flash enables */
	if (pci_init_common() != 0)
		return 1;

	if (processor_flash_enable()) {
		msg_perr("Processor detection/init failed.\n"
			 "Aborting.\n");
		return 1;
	}

#if defined(__i386__) || defined(__x86_64__) || defined (__arm__)
	if ((cb_parse_table(&cb_vendor, &cb_model) == 0) && (board_vendor != NULL) && (board_model != NULL)) {
		if (strcasecmp(board_vendor, cb_vendor) || strcasecmp(board_model, cb_model)) {
			msg_pwarn("Warning: The mainboard IDs set by -p internal:mainboard (%s:%s) do not\n"
				  "         match the current coreboot IDs of the mainboard (%s:%s).\n",
				  board_vendor, board_model, cb_vendor, cb_model);
			if (!force_boardmismatch)
				return 1;
			msg_pinfo("Continuing anyway.\n");
		}
	}
#endif

#if defined(__i386__) || defined(__x86_64__)
	dmi_init();

	/* In case Super I/O probing would cause pretty explosions. */
	board_handle_before_superio();

	/* Probe for the Super I/O chip and fill global struct superio. */
	probe_superio();
#else
	/* FIXME: Enable cbtable searching on all non-x86 platforms supported
	 *        by coreboot.
	 * FIXME: Find a replacement for DMI on non-x86.
	 * FIXME: Enable Super I/O probing once port I/O is possible.
	 */
#endif

	/* Check laptop whitelist. */
	board_handle_before_laptop();

	/* Warn if a non-whitelisted laptop is detected. */
	if (is_laptop && !laptop_ok) {
		msg_perr("========================================================================\n");
		if (is_laptop == 1) {
			msg_perr("WARNING! You seem to be running flashrom on an unsupported laptop.\n");
		} else {
			msg_perr("WARNING! You may be running flashrom on an unsupported laptop. We could\n"
				 "not detect this for sure because your vendor has not setup the SMBIOS\n"
				 "tables correctly. You can enforce execution by adding\n"
				 "'-p internal:laptop=this_is_not_a_laptop' to the command line, but\n"
				 "please read the following warning if you are not sure.\n\n");
		}
		msg_perr("Laptops, notebooks and netbooks are difficult to support and we\n"
			 "recommend to use the vendor flashing utility. The embedded controller\n"
			 "(EC) in these machines often interacts badly with flashing.\n"
			 "See the manpage and http://www.flashrom.org/Laptops for details.\n\n"
			 "If flash is shared with the EC, erase is guaranteed to brick your laptop\n"
			 "and write may brick your laptop.\n"
			 "Read and probe may irritate your EC and cause fan failure, backlight\n"
			 "failure and sudden poweroff.\n"
			 "You have been warned.\n"
			 "========================================================================\n");

		if (force_laptop || (not_a_laptop && (is_laptop == 2))) {
			msg_perr("Proceeding anyway because user forced us to.\n");
		} else {
			msg_perr("Aborting.\n");
			return 1;
		}
	}

#if __FLASHROM_LITTLE_ENDIAN__
	/* try to enable it. Failure IS an option, since not all motherboards
	 * really need this to be done, etc., etc.
	 */
	ret = chipset_flash_enable();
	if (ret == -2) {
		msg_perr("WARNING: No chipset found. Flash detection "
			 "will most likely fail.\n");
	} else if (ret == ERROR_FATAL)
		return ret;

#if defined(__i386__) || defined(__x86_64__)
	/* Probe unconditionally for ITE Super I/O chips. This enables LPC->SPI translation on IT87* and
	 * parallel writes on IT8705F. Also, this handles the manual chip select for Gigabyte's DualBIOS. */
	init_superio_ite();
#endif

	if (board_flash_enable(board_vendor, board_model, cb_vendor, cb_model)) {
		msg_perr("Aborting to be safe.\n");
		return 1;
	}

#if defined(__i386__) || defined(__x86_64__) || defined (__mips)
	register_par_master(&par_master_internal, internal_buses_supported);
	return 0;
#else
	msg_perr("Your platform is not supported yet for the internal "
		 "programmer due to missing\n"
		 "flash_base and top/bottom alignment information.\n"
		 "Aborting.\n");
	return 1;
#endif
#else
	/* FIXME: Remove this unconditional abort once all PCI drivers are
	 * converted to use little-endian accesses for memory BARs.
	 */
	msg_perr("Your platform is not supported yet for the internal "
		 "programmer because it has\n"
		 "not been converted from native endian to little endian "
		 "access yet.\n"
		 "Aborting.\n");
	return 1;
#endif
}
示例#15
0
static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
				  unsigned int readcnt,
				  const unsigned char *writearr,
				  unsigned char *readarr)
{
	int count;
	/* First byte is cmd which can not being sent through FIFO. */
	unsigned char cmd = *writearr++;
	unsigned int readoffby1;
	unsigned char readwrite;

	writecnt--;

	msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n",
		  __func__, cmd, writecnt, readcnt);

	if (readcnt > 8) {
		msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, "
		       "it is limited to 8 bytes\n", __func__, readcnt);
		return SPI_INVALID_LENGTH;
	}

	if (writecnt > 8) {
		msg_pinfo("%s, SB600 SPI controller can not send %d bytes, "
		       "it is limited to 8 bytes\n", __func__, writecnt);
		return SPI_INVALID_LENGTH;
	}

	/* This is a workaround for a bug in SB600 and SB700. If we only send
	 * an opcode and no additional data/address, the SPI controller will
	 * read one byte too few from the chip. Basically, the last byte of
	 * the chip response is discarded and will not end up in the FIFO.
	 * It is unclear if the CS# line is set high too early as well.
	 */
	readoffby1 = (writecnt) ? 0 : 1;
	readwrite = (readcnt + readoffby1) << 4 | (writecnt);
	mmio_writeb(readwrite, sb600_spibar + 1);
	mmio_writeb(cmd, sb600_spibar + 0);

	/* Before we use the FIFO, reset it first. */
	reset_internal_fifo_pointer();

	/* Send the write byte to FIFO. */
	msg_pspew("Writing: ");
	for (count = 0; count < writecnt; count++, writearr++) {
		msg_pspew("[%02x]", *writearr);
		mmio_writeb(*writearr, sb600_spibar + 0xC);
	}
	msg_pspew("\n");

	/*
	 * We should send the data by sequence, which means we need to reset
	 * the FIFO pointer to the first byte we want to send.
	 */
	if (reset_compare_internal_fifo_pointer(writecnt))
		return SPI_PROGRAMMER_ERROR;

	msg_pspew("Executing: \n");
	execute_command();

	/*
	 * After the command executed, we should find out the index of the
	 * received byte. Here we just reset the FIFO pointer and skip the
	 * writecnt.
	 * It would be possible to increase the FIFO pointer by one instead
	 * of reading and discarding one byte from the FIFO.
	 * The FIFO is implemented on top of an 8 byte ring buffer and the
	 * buffer is never cleared. For every byte that is shifted out after
	 * the opcode, the FIFO already stores the response from the chip.
	 * Usually, the chip will respond with 0x00 or 0xff.
	 */
	if (reset_compare_internal_fifo_pointer(writecnt + readcnt))
		return SPI_PROGRAMMER_ERROR;

	/* Skip the bytes we sent. */
	msg_pspew("Skipping: ");
	for (count = 0; count < writecnt; count++) {
		cmd = mmio_readb(sb600_spibar + 0xC);
		msg_pspew("[%02x]", cmd);
	}
	msg_pspew("\n");
	if (compare_internal_fifo_pointer(writecnt))
		return SPI_PROGRAMMER_ERROR;

	msg_pspew("Reading: ");
	for (count = 0; count < readcnt; count++, readarr++) {
		*readarr = mmio_readb(sb600_spibar + 0xC);
		msg_pspew("[%02x]", *readarr);
	}
	msg_pspew("\n");
	if (reset_compare_internal_fifo_pointer(readcnt + writecnt))
		return SPI_PROGRAMMER_ERROR;

	if (mmio_readb(sb600_spibar + 1) != readwrite) {
		msg_perr("Unexpected change in SB600 read/write count!\n");
		msg_perr("Something else is accessing the flash chip and "
			 "causes random corruption.\nPlease stop all "
			 "applications and drivers and IPMI which access the "
			 "flash chip.\n");
		return SPI_PROGRAMMER_ERROR;
	}

	return 0;
}
示例#16
0
int mcp6x_spi_init(int want_spi)
{
	uint16_t status;
	uint32_t mcp6x_spibaraddr;
	struct pci_dev *smbusdev;

	/* Look for the SMBus device (SMBus PCI class) */
	smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
	if (!smbusdev) {
		if (want_spi) {
			msg_perr("ERROR: SMBus device not found. Not enabling "
				 "SPI.\n");
			return 1;
		} else {
			msg_pinfo("Odd. SMBus device not found.\n");
			return 0;
		}
	}
	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
		smbusdev->vendor_id, smbusdev->device_id,
		smbusdev->bus, smbusdev->dev, smbusdev->func);


	/* Locate the BAR where the SPI interface lives. */
	mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
	/* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
	 * 32-bit non-prefetchable memory BAR.
	 */
	mcp6x_spibaraddr &= ~0xffff;
	msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);

	/* Accessing a NULL pointer BAR is evil. Don't do it. */
	if (!mcp6x_spibaraddr && want_spi) {
		msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
		return 1;
	} else if (!mcp6x_spibaraddr && !want_spi) {
		msg_pdbg("MCP SPI is not used.\n");
		return 0;
	} else if (mcp6x_spibaraddr && !want_spi) {
		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
		/* FIXME: Should we enable SPI anyway? */
		return 0;
	}
	/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
	mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
	if (mcp6x_spibar == ERROR_PTR)
		return 1;

	status = mmio_readw(mcp6x_spibar + 0x530);
	msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
		 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
		 (status >> MCP6X_SPI_GRANT) & 0x1);
	mcp_gpiostate = status & 0xff;

	if (bitbang_spi_init(&bitbang_spi_master_mcp6x)) {
		/* This should never happen. */
		msg_perr("MCP6X bitbang SPI master init failed!\n");
		return 1;
	}

	return 0;
}
示例#17
0
/* Returns 0 upon success, a negative number upon errors. */
int mstarddc_spi_init(void)
{
	int ret = 0;

	// Get device, address from command-line
	char *i2c_device = extract_programmer_param("dev");
	if (i2c_device != NULL && strlen(i2c_device) > 0) {
		char *i2c_address = strchr(i2c_device, ':');
		if (i2c_address != NULL) {
			*i2c_address = '\0';
			i2c_address++;
		}
		if (i2c_address == NULL || strlen(i2c_address) == 0) {
			msg_perr("Error: no address specified.\n"
				 "Use flashrom -p mstarddc_spi:dev=/dev/device:address.\n");
			ret = -1;
			goto out;
		}
		mstarddc_addr = strtol(i2c_address, NULL, 16); // FIXME: error handling
	} else {
		msg_perr("Error: no device specified.\n"
			 "Use flashrom -p mstarddc_spi:dev=/dev/device:address.\n");
		ret = -1;
		goto out;
	}
	msg_pinfo("Info: Will try to use device %s and address 0x%02x.\n", i2c_device, mstarddc_addr);

	// Get noreset=1 option from command-line
	char *noreset = extract_programmer_param("noreset");
	if (noreset != NULL && noreset[0] == '1')
		mstarddc_doreset = 0;
	free(noreset);
	msg_pinfo("Info: Will %sreset the device at the end.\n", mstarddc_doreset ? "" : "NOT ");
	// Open device
	if ((mstarddc_fd = open(i2c_device, O_RDWR)) < 0) {
		switch (errno) {
		case EACCES:
			msg_perr("Error opening %s: Permission denied.\n"
				 "Please use sudo or run as root.\n",
				 i2c_device);
			break;
		case ENOENT:
			msg_perr("Error opening %s: No such file.\n"
				 "Please check you specified the correct device.\n",
				 i2c_device);
			break;
		default:
			msg_perr("Error opening %s: %s.\n", i2c_device, strerror(errno));
		}
		ret = -1;
		goto out;
	}
	// Set slave address
	if (ioctl(mstarddc_fd, I2C_SLAVE, mstarddc_addr) < 0) {
		msg_perr("Error setting slave address 0x%02x: errno %d.\n",
			 mstarddc_addr, errno);
		ret = -1;
		goto out;
	}
	// Enable ISP mode
	uint8_t cmd[5] = { 'M', 'S', 'T', 'A', 'R' };
	if (write(mstarddc_fd, cmd, 5) < 0) {
		int enable_err = errno;
		uint8_t end_cmd = MSTARDDC_SPI_END;

		// Assume device is already in ISP mode, try to send END command
		if (write(mstarddc_fd, &end_cmd, 1) < 0) {
			msg_perr("Error enabling ISP mode: errno %d & %d.\n"
				 "Please check that device (%s) and address (0x%02x) are correct.\n",
				 enable_err, errno, i2c_device, mstarddc_addr);
			ret = -1;
			goto out;
		}
	}
	// Register shutdown function
	register_shutdown(mstarddc_spi_shutdown, NULL);

	// Register programmer
	register_spi_master(&spi_master_mstarddc);
out:
	free(i2c_device);
	return ret;
}
示例#18
0
static uint16_t it87spi_probe(uint16_t port)
{
	uint8_t tmp = 0;
	char *portpos = NULL;
	uint16_t flashport = 0;

	enter_conf_mode_ite(port);
	/* NOLDN, reg 0x24, mask out lowest bit (suspend) */
	tmp = sio_read(port, 0x24) & 0xFE;
	/* If IT87SPI was not explicitly selected, we want to check
	 * quickly if LPC->SPI translation is active.
	 */
	if ((programmer == PROGRAMMER_INTERNAL) && !(tmp & (0x0E))) {
		msg_pdbg("No IT87* serial flash segment enabled.\n");
		exit_conf_mode_ite(port);
		/* Nothing to do. */
		return 0;
	}
	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
		 0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis");
	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
		 0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis");
	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
		 0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis");
	msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
		 0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis");
	msg_pdbg("LPC write to serial flash %sabled\n",
		 (tmp & 1 << 4) ? "en" : "dis");
	/* The LPC->SPI force write enable below only makes sense for
	 * non-programmer mode.
	 */
	/* If any serial flash segment is enabled, enable writing. */
	if ((tmp & 0xe) && (!(tmp & 1 << 4))) {
		msg_pdbg("Enabling LPC write to serial flash\n");
		tmp |= 1 << 4;
		sio_write(port, 0x24, tmp);
	}
	msg_pdbg("Serial flash pin %i\n", (tmp & 1 << 5) ? 87 : 29);
	/* LDN 0x7, reg 0x64/0x65 */
	sio_write(port, 0x07, 0x7);
	flashport = sio_read(port, 0x64) << 8;
	flashport |= sio_read(port, 0x65);
	msg_pdbg("Serial flash port 0x%04x\n", flashport);
	/* Non-default port requested? */
	portpos = extract_programmer_param("it87spiport");
	if (portpos) {
		char *endptr = NULL;
		unsigned long forced_flashport;
		forced_flashport = strtoul(portpos, &endptr, 0);
		/* Port 0, port >0x1000, unaligned ports and garbage strings
		 * are rejected.
		 */
		if (!forced_flashport || (forced_flashport >= 0x1000) ||
		    (forced_flashport & 0x7) || (*endptr != '\0')) {
			/* Using ports below 0x100 is a really bad idea, and
			 * should only be done if no port between 0x100 and
			 * 0xff8 works due to routing issues.
			 */
			msg_perr("Error: it87spiport specified, but no valid "
				 "port specified.\nPort must be a multiple of "
				 "0x8 and lie between 0x100 and 0xff8.\n");
			free(portpos);
			return 1;
		} else {
			flashport = (uint16_t)forced_flashport;
			msg_pinfo("Forcing serial flash port 0x%04x\n",
				  flashport);
			sio_write(port, 0x64, (flashport >> 8));
			sio_write(port, 0x65, (flashport & 0xff));
		}
	}
	free(portpos);
	exit_conf_mode_ite(port);
	it8716f_flashport = flashport;
	if (buses_supported & CHIP_BUSTYPE_SPI)
		msg_pdbg("Overriding chipset SPI with IT87 SPI.\n");
	/* FIXME: Add the SPI bus or replace the other buses with it? */
	register_spi_programmer(&spi_programmer_it87xx);
	return 0;
}