int smm_load_module(void *smram, size_t size, struct smm_loader_params *params)
{
	struct rmodule smm_mod;
	size_t total_stack_size;
	size_t handler_size;
	size_t module_alignment;
	size_t alignment_size;
	size_t fxsave_size;
	void *fxsave_area;
	size_t total_size = 0;
	char *base;

	if (size <= SMM_DEFAULT_SIZE)
		return -1;

	/* Load main SMI handler at the top of SMRAM
	 * everything else will go below
	 */
	base = smram;
	base += size;
	params->smram_start = (uintptr_t)smram;
	params->smram_end = params->smram_start + size;
	params->smm_main_entry_offset = SMM_ENTRY_OFFSET;

	/* Fail if can't parse the smm rmodule. */
	if (rmodule_parse(&_binary_smm_start, &smm_mod))
		return -1;

	/* Clear SMM region */
	if (CONFIG(DEBUG_SMI))
		memset(smram, 0xcd, size);

	total_stack_size = params->per_cpu_stack_size *
			   params->num_concurrent_stacks;
	total_size += total_stack_size;
	/* Stacks are the base of SMRAM */
	params->stack_top = smram + total_stack_size;

	/* MSEG starts at the top of SMRAM and works down */
	if (CONFIG(STM)) {
		base -= CONFIG_MSEG_SIZE + CONFIG_BIOS_RESOURCE_LIST_SIZE;
		total_size += CONFIG_MSEG_SIZE + CONFIG_BIOS_RESOURCE_LIST_SIZE;
	}

	/* FXSAVE goes below MSEG */
	if (CONFIG(SSE)) {
		fxsave_size = FXSAVE_SIZE * params->num_concurrent_stacks;
		fxsave_area = base - fxsave_size;
		base -= fxsave_size;
		total_size += fxsave_size;
	} else {
		fxsave_size = 0;
		fxsave_area = NULL;
	}


	handler_size = rmodule_memory_size(&smm_mod);
	base -= handler_size;
	total_size += handler_size;
	module_alignment = rmodule_load_alignment(&smm_mod);
	alignment_size = module_alignment -
				((uintptr_t)base % module_alignment);
	if (alignment_size != module_alignment) {
		handler_size += alignment_size;
		base += alignment_size;
	}

	printk(BIOS_DEBUG,
		"%s: total_smm_space_needed %zx, available -> %zx\n",
		 __func__, total_size, size);

	/* Does the required amount of memory exceed the SMRAM region size? */
	if (total_size > size) {
		printk(BIOS_ERR, "%s: need more SMRAM\n", __func__);
		return -1;
	}
	if (handler_size > SMM_CODE_SEGMENT_SIZE) {
		printk(BIOS_ERR, "%s: increase SMM_CODE_SEGMENT_SIZE: handler_size = %zx\n",
			__func__, handler_size);
		return -1;
	}

	if (rmodule_load(base, &smm_mod))
		return -1;

	params->handler = rmodule_entry(&smm_mod);
	params->handler_arg = rmodule_parameters(&smm_mod);

	printk(BIOS_DEBUG, "%s: smram_start: 0x%p\n",
		 __func__, smram);
	printk(BIOS_DEBUG, "%s: smram_end: %p\n",
		 __func__, smram + size);
	printk(BIOS_DEBUG, "%s: stack_top: %p\n",
		 __func__, params->stack_top);
	printk(BIOS_DEBUG, "%s: handler start %p\n",
		 __func__, params->handler);
	printk(BIOS_DEBUG, "%s: handler_size %zx\n",
		 __func__, handler_size);
	printk(BIOS_DEBUG, "%s: handler_arg %p\n",
		 __func__, params->handler_arg);
	printk(BIOS_DEBUG, "%s: fxsave_area %p\n",
		 __func__, fxsave_area);
	printk(BIOS_DEBUG, "%s: fxsave_size %zx\n",
		 __func__, fxsave_size);
	printk(BIOS_DEBUG, "%s: CONFIG_MSEG_SIZE 0x%x\n",
		 __func__, CONFIG_MSEG_SIZE);
	printk(BIOS_DEBUG, "%s: CONFIG_BIOS_RESOURCE_LIST_SIZE 0x%x\n",
		 __func__, CONFIG_BIOS_RESOURCE_LIST_SIZE);

	/* CPU 0 smbase goes first, all other CPUs
	 * will be staggered below
	 */
	base -= SMM_CODE_SEGMENT_SIZE;
	printk(BIOS_DEBUG, "%s: cpu0 entry: %p\n",
		 __func__, base);
	params->smm_entry = (uintptr_t)base + params->smm_main_entry_offset;
	return smm_module_setup_stub(base, size, params, fxsave_area);
}