static __init int dev_mcelog_init_device(void)
{
	int err;

	/* register character device /dev/mcelog */
	err = misc_register(&mce_chrdev_device);
	if (err) {
		pr_err("Unable to init device /dev/mcelog (rc: %d)\n", err);
		return err;
	}
	mce_register_decode_chain(&dev_mcelog_nb);
	return 0;
}
static int __init extlog_init(void)
{
	struct extlog_l1_head *l1_head;
	void __iomem *extlog_l1_hdr;
	size_t l1_hdr_size;
	struct resource *r;
	u64 cap;
	int rc;

	rc = -ENODEV;

	rdmsrl(MSR_IA32_MCG_CAP, cap);
	if (!(cap & MCG_ELOG_P))
		return rc;

	if (!extlog_get_l1addr())
		return rc;

	rc = -EINVAL;
	/* get L1 header to fetch necessary information */
	l1_hdr_size = sizeof(struct extlog_l1_head);
	r = request_mem_region(l1_dirbase, l1_hdr_size, "L1 DIR HDR");
	if (!r) {
		pr_warn(FW_BUG EMCA_BUG,
			(unsigned long long)l1_dirbase,
			(unsigned long long)l1_dirbase + l1_hdr_size);
		goto err;
	}

	extlog_l1_hdr = acpi_os_map_memory(l1_dirbase, l1_hdr_size);
	l1_head = (struct extlog_l1_head *)extlog_l1_hdr;
	l1_size = l1_head->total_len;
	l1_percpu_entry = l1_head->entries;
	elog_base = l1_head->elog_base;
	elog_size = l1_head->elog_len;
	acpi_os_unmap_memory(extlog_l1_hdr, l1_hdr_size);
	release_mem_region(l1_dirbase, l1_hdr_size);

	/* remap L1 header again based on completed information */
	r = request_mem_region(l1_dirbase, l1_size, "L1 Table");
	if (!r) {
		pr_warn(FW_BUG EMCA_BUG,
			(unsigned long long)l1_dirbase,
			(unsigned long long)l1_dirbase + l1_size);
		goto err;
	}
	extlog_l1_addr = acpi_os_map_memory(l1_dirbase, l1_size);
	l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size);

	/* remap elog table */
	r = request_mem_region(elog_base, elog_size, "Elog Table");
	if (!r) {
		pr_warn(FW_BUG EMCA_BUG,
			(unsigned long long)elog_base,
			(unsigned long long)elog_base + elog_size);
		goto err_release_l1_dir;
	}
	elog_addr = acpi_os_map_memory(elog_base, elog_size);

	rc = -ENOMEM;
	/* allocate buffer to save elog record */
	elog_buf = kmalloc(ELOG_ENTRY_LEN, GFP_KERNEL);
	if (elog_buf == NULL)
		goto err_release_elog;

	mce_register_decode_chain(&extlog_mce_dec);
	/* enable OS to be involved to take over management from BIOS */
	((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN;

	return 0;

err_release_elog:
	if (elog_addr)
		acpi_os_unmap_memory(elog_addr, elog_size);
	release_mem_region(elog_base, elog_size);
err_release_l1_dir:
	if (extlog_l1_addr)
		acpi_os_unmap_memory(extlog_l1_addr, l1_size);
	release_mem_region(l1_dirbase, l1_size);
err:
	pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n");
	return rc;
}