1038 lines
25 KiB
C
1038 lines
25 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/arch/sw/kernel/setup.c
|
|
*
|
|
* Copyright (C) 1995 Linus Torvalds
|
|
*/
|
|
|
|
/*
|
|
* Bootup setup stuff.
|
|
*/
|
|
|
|
#include <linux/screen_info.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/kexec.h>
|
|
#include <linux/console.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/root_dev.h>
|
|
#include <linux/initrd.h>
|
|
#ifdef CONFIG_MAGIC_SYSRQ
|
|
#include <linux/sysrq.h>
|
|
#include <linux/reboot.h>
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_FS
|
|
#include <linux/debugfs.h>
|
|
#endif
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/genalloc.h>
|
|
#include <linux/acpi.h>
|
|
|
|
#include <asm/sw64_init.h>
|
|
#include <asm/efi.h>
|
|
#include <asm/kvm_cma.h>
|
|
|
|
#include "proto.h"
|
|
#include "pci_impl.h"
|
|
|
|
#undef DEBUG_DISCONTIG
|
|
#ifdef DEBUG_DISCONTIG
|
|
#define DBGDCONT(args...) pr_debug(args)
|
|
#else
|
|
#define DBGDCONT(args...)
|
|
#endif
|
|
|
|
|
|
DEFINE_PER_CPU(unsigned long, hard_node_id) = { 0 };
|
|
|
|
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
|
|
struct cma *sw64_kvm_cma;
|
|
EXPORT_SYMBOL(sw64_kvm_cma);
|
|
|
|
static phys_addr_t kvm_mem_size;
|
|
static phys_addr_t kvm_mem_base;
|
|
|
|
struct gen_pool *sw64_kvm_pool;
|
|
EXPORT_SYMBOL(sw64_kvm_pool);
|
|
#endif
|
|
|
|
static inline int phys_addr_valid(unsigned long addr)
|
|
{
|
|
/*
|
|
* At this point memory probe has not been done such that max_pfn
|
|
* and other physical address variables cannnot be used, so let's
|
|
* roughly judge physical address based on arch specific bit.
|
|
*/
|
|
return !(addr >> (cpu_desc.pa_bits - 1));
|
|
}
|
|
|
|
extern struct atomic_notifier_head panic_notifier_list;
|
|
static int sw64_panic_event(struct notifier_block *, unsigned long, void *);
|
|
static struct notifier_block sw64_panic_block = {
|
|
sw64_panic_event,
|
|
NULL,
|
|
INT_MAX /* try to do it first */
|
|
};
|
|
|
|
/* the value is IOR: CORE_ONLIE*/
|
|
cpumask_t core_start = CPU_MASK_NONE;
|
|
|
|
static struct resource data_resource = {
|
|
.name = "Kernel data",
|
|
.start = 0,
|
|
.end = 0,
|
|
.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM
|
|
};
|
|
|
|
static struct resource code_resource = {
|
|
.name = "Kernel code",
|
|
.start = 0,
|
|
.end = 0,
|
|
.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM
|
|
};
|
|
|
|
static struct resource bss_resource = {
|
|
.name = "Kernel bss",
|
|
.start = 0,
|
|
.end = 0,
|
|
.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM
|
|
};
|
|
|
|
/* A collection of per-processor data. */
|
|
struct cpuinfo_sw64 cpu_data[NR_CPUS];
|
|
EXPORT_SYMBOL(cpu_data);
|
|
|
|
DEFINE_STATIC_KEY_TRUE(run_mode_host_key);
|
|
DEFINE_STATIC_KEY_FALSE(run_mode_guest_key);
|
|
DEFINE_STATIC_KEY_FALSE(run_mode_emul_key);
|
|
struct cpu_desc_t cpu_desc;
|
|
struct socket_desc_t socket_desc[MAX_NUMSOCKETS];
|
|
int memmap_nr;
|
|
struct memmap_entry memmap_map[MAX_NUMMEMMAPS];
|
|
bool memblock_initialized;
|
|
|
|
cpumask_t cpu_offline = CPU_MASK_NONE;
|
|
|
|
static char command_line[COMMAND_LINE_SIZE] __initdata;
|
|
#ifdef CONFIG_CMDLINE_BOOL
|
|
static char builtin_cmdline[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
|
|
#endif
|
|
|
|
/* boot_params */
|
|
struct boot_params *sunway_boot_params = (struct boot_params *) (PARAM + 0x100);
|
|
|
|
/*
|
|
* The format of "screen_info" is strange, and due to early
|
|
* i386-setup code. This is just enough to make the console
|
|
* code think we're on a VGA color display.
|
|
*/
|
|
|
|
struct screen_info screen_info = {
|
|
.orig_x = 0,
|
|
.orig_y = 25,
|
|
.orig_video_cols = 80,
|
|
.orig_video_lines = 25,
|
|
.orig_video_isVGA = 1,
|
|
.orig_video_points = 16
|
|
};
|
|
EXPORT_SYMBOL(screen_info);
|
|
|
|
#ifdef CONFIG_KEXEC
|
|
|
|
void *kexec_control_page;
|
|
|
|
#define KTEXT_MAX KERNEL_IMAGE_SIZE
|
|
|
|
static void __init kexec_control_page_init(void)
|
|
{
|
|
phys_addr_t addr;
|
|
|
|
addr = memblock_phys_alloc_range(KEXEC_CONTROL_PAGE_SIZE, PAGE_SIZE,
|
|
0, KTEXT_MAX);
|
|
kexec_control_page = (void *)(__START_KERNEL_map + addr);
|
|
}
|
|
|
|
/*
|
|
* reserve_crashkernel() - reserves memory are for crash kernel
|
|
*
|
|
* This function reserves memory area given in "crashkernel=" kernel command
|
|
* line parameter. The memory reserved is used by a dump capture kernel when
|
|
* primary kernel is crashing.
|
|
*/
|
|
static void __init reserve_crashkernel(void)
|
|
{
|
|
unsigned long long crash_size, crash_base;
|
|
int ret;
|
|
|
|
ret = parse_crashkernel(boot_command_line, mem_desc.size,
|
|
&crash_size, &crash_base);
|
|
if (ret || !crash_size)
|
|
return;
|
|
|
|
if (!memblock_is_region_memory(crash_base, crash_size))
|
|
memblock_add(crash_base, crash_size);
|
|
|
|
ret = memblock_reserve(crash_base, crash_size);
|
|
if (ret < 0) {
|
|
pr_warn("crashkernel reservation failed - memory is in use [mem %#018llx-%#018llx]\n",
|
|
crash_base, crash_base + crash_size - 1);
|
|
return;
|
|
}
|
|
|
|
pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n",
|
|
(unsigned long)(crash_size >> 20),
|
|
(unsigned long)(crash_base >> 20),
|
|
(unsigned long)(mem_desc.size >> 20));
|
|
|
|
ret = add_memmap_region(crash_base, crash_size, memmap_crashkernel);
|
|
if (ret)
|
|
pr_warn("Add crash kernel area [mem %#018llx-%#018llx] to memmap region failed.\n",
|
|
crash_base, crash_base + crash_size - 1);
|
|
|
|
if (crash_base >= KERNEL_IMAGE_SIZE)
|
|
pr_warn("Crash base should be less than %#x\n", KERNEL_IMAGE_SIZE);
|
|
|
|
crashk_res.start = crash_base;
|
|
crashk_res.end = crash_base + crash_size - 1;
|
|
insert_resource(&iomem_resource, &crashk_res);
|
|
}
|
|
#else /* !defined(CONFIG_KEXEC) */
|
|
static void __init reserve_crashkernel(void) {}
|
|
static void __init kexec_control_page_init(void) {}
|
|
#endif /* !defined(CONFIG_KEXEC) */
|
|
|
|
/*
|
|
* I/O resources inherited from PeeCees. Except for perhaps the
|
|
* turbochannel SWs, everyone has these on some sort of SuperIO chip.
|
|
*
|
|
* ??? If this becomes less standard, move the struct out into the
|
|
* machine vector.
|
|
*/
|
|
|
|
static void __init
|
|
reserve_std_resources(void)
|
|
{
|
|
static struct resource standard_io_resources[] = {
|
|
{ .name = "rtc", .start = -1, .end = -1 },
|
|
{ .name = "dma1", .start = 0x00, .end = 0x1f },
|
|
{ .name = "pic1", .start = 0x20, .end = 0x3f },
|
|
{ .name = "timer", .start = 0x40, .end = 0x5f },
|
|
{ .name = "keyboard", .start = 0x60, .end = 0x6f },
|
|
{ .name = "dma page reg", .start = 0x80, .end = 0x8f },
|
|
{ .name = "pic2", .start = 0xa0, .end = 0xbf },
|
|
{ .name = "dma2", .start = 0xc0, .end = 0xdf },
|
|
};
|
|
|
|
struct resource *io = &ioport_resource;
|
|
size_t i;
|
|
|
|
if (hose_head) {
|
|
struct pci_controller *hose;
|
|
|
|
for (hose = hose_head; hose; hose = hose->next)
|
|
if (hose->index == 0) {
|
|
io = hose->io_space;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Fix up for the Jensen's queer RTC placement. */
|
|
standard_io_resources[0].start = RTC_PORT(0);
|
|
standard_io_resources[0].end = RTC_PORT(0) + 0x10;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(standard_io_resources); ++i)
|
|
request_resource(io, standard_io_resources+i);
|
|
}
|
|
|
|
static int __init parse_memmap_one(char *p)
|
|
{
|
|
char *oldp;
|
|
u64 start_at, mem_size;
|
|
int ret;
|
|
|
|
if (!p)
|
|
return -EINVAL;
|
|
|
|
if (!strncmp(p, "exactmap", 8)) {
|
|
pr_err("\"memmap=exactmap\" not valid on sw64\n");
|
|
return 0;
|
|
}
|
|
|
|
oldp = p;
|
|
mem_size = memparse(p, &p);
|
|
if (p == oldp)
|
|
return -EINVAL;
|
|
|
|
if (*p == '@') {
|
|
pr_err("\"memmap=nn@ss\" invalid on sw64\n");
|
|
} else if (*p == '#') {
|
|
pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on sw64\n");
|
|
} else if (*p == '$') {
|
|
start_at = memparse(p + 1, &p);
|
|
ret = add_memmap_region(start_at, mem_size, memmap_reserved);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
return *p == '\0' ? 0 : -EINVAL;
|
|
}
|
|
|
|
static int __init setup_memmap(char *str)
|
|
{
|
|
while (str) {
|
|
char *k = strchr(str, ',');
|
|
|
|
if (k)
|
|
*k++ = 0;
|
|
|
|
parse_memmap_one(str);
|
|
str = k;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
early_param("memmap", setup_memmap);
|
|
|
|
static int __init setup_cpuoffline(char *p)
|
|
{
|
|
cpulist_parse(p, &cpu_offline);
|
|
cpumask_clear_cpu(0, &cpu_offline);
|
|
return 0;
|
|
}
|
|
early_param("cpuoffline", setup_cpuoffline);
|
|
|
|
#ifdef CONFIG_BLK_DEV_INITRD
|
|
static void * __init move_initrd(unsigned long mem_limit)
|
|
{
|
|
void *start;
|
|
unsigned long size;
|
|
|
|
size = initrd_end - initrd_start;
|
|
start = memblock_alloc_from(PAGE_ALIGN(size), PAGE_SIZE, 0);
|
|
if (!start || __pa(start) + size > mem_limit) {
|
|
initrd_start = initrd_end = 0;
|
|
return NULL;
|
|
}
|
|
memmove(start, (void *)initrd_start, size);
|
|
initrd_start = (unsigned long)start;
|
|
initrd_end = initrd_start + size;
|
|
pr_info("initrd moved to 0x%px\n", start);
|
|
return start;
|
|
}
|
|
#else
|
|
static void * __init move_initrd(unsigned long mem_limit)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static int __init memmap_range_valid(phys_addr_t base, phys_addr_t size)
|
|
{
|
|
if ((base + size) <= memblock_end_of_DRAM())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void __init process_memmap(void)
|
|
{
|
|
static int i; // Make it static so we won't start over again every time.
|
|
int ret;
|
|
phys_addr_t base, size;
|
|
unsigned long dma_end __maybe_unused = virt_to_phys((void *)MAX_DMA_ADDRESS);
|
|
|
|
if (!memblock_initialized)
|
|
return;
|
|
|
|
for (; i < memmap_nr; i++) {
|
|
base = memmap_map[i].addr;
|
|
size = memmap_map[i].size;
|
|
switch (memmap_map[i].type) {
|
|
case memmap_reserved:
|
|
if (!memmap_range_valid(base, size)) {
|
|
pr_err("reserved memmap region [mem %#018llx-%#018llx] extends beyond end of memory (%#018llx)\n",
|
|
base, base + size - 1, memblock_end_of_DRAM());
|
|
} else {
|
|
pr_info("reserved memmap region [mem %#018llx-%#018llx]\n",
|
|
base, base + size - 1);
|
|
ret = memblock_mark_nomap(base, size);
|
|
if (ret)
|
|
pr_err("reserve memmap region [mem %#018llx-%#018llx] failed\n",
|
|
base, base + size - 1);
|
|
else if (IS_ENABLED(CONFIG_ZONE_DMA32) && (base < dma_end))
|
|
pr_warn("memmap region [mem %#018llx-%#018llx] overlapped with DMA32 region\n",
|
|
base, base + size - 1);
|
|
}
|
|
break;
|
|
case memmap_pci:
|
|
if (!memmap_range_valid(base, size)) {
|
|
pr_info("pci memmap region [mem %#018llx-%#018llx] extends beyond end of memory (%#018llx)\n",
|
|
base, base + size - 1, memblock_end_of_DRAM());
|
|
} else {
|
|
pr_info("pci memmap region [mem %#018llx-%#018llx]\n",
|
|
base, base + size - 1);
|
|
ret = memblock_mark_nomap(base, size);
|
|
if (ret)
|
|
pr_err("reserve memmap region [mem %#018llx-%#018llx] failed\n",
|
|
base, base + size - 1);
|
|
}
|
|
break;
|
|
case memmap_initrd:
|
|
if (!memmap_range_valid(base, size)) {
|
|
phys_addr_t old_base = base;
|
|
|
|
base = (unsigned long) move_initrd(memblock_end_of_DRAM());
|
|
if (!base) {
|
|
pr_err("initrd memmap region [mem %#018llx-%#018llx] extends beyond end of memory (%#018llx)\n",
|
|
old_base, old_base + size - 1, memblock_end_of_DRAM());
|
|
} else {
|
|
memmap_map[i].addr = base;
|
|
pr_info("initrd memmap region [mem %#018llx-%#018llx]\n",
|
|
base, base + size - 1);
|
|
ret = memblock_reserve(base, size);
|
|
if (ret)
|
|
pr_err("reserve memmap region [mem %#018llx-%#018llx] failed\n",
|
|
base, base + size - 1);
|
|
}
|
|
} else {
|
|
pr_info("initrd memmap region [mem %#018llx-%#018llx]\n", base, base + size - 1);
|
|
ret = memblock_reserve(base, size);
|
|
if (ret)
|
|
pr_err("reserve memmap region [mem %#018llx-%#018llx] failed\n",
|
|
base, base + size - 1);
|
|
}
|
|
break;
|
|
case memmap_kvm:
|
|
case memmap_crashkernel:
|
|
/* kvm and crashkernel are handled elsewhere, skip */
|
|
break;
|
|
case memmap_acpi:
|
|
pr_err("ACPI memmap region is not supported.\n");
|
|
break;
|
|
case memmap_use:
|
|
pr_err("Force usage memmap region is not supported.\n");
|
|
break;
|
|
case memmap_protected:
|
|
pr_err("Protected memmap region is not supported.\n");
|
|
break;
|
|
default:
|
|
pr_err("Unknown type of memmap region.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
int __init add_memmap_region(u64 addr, u64 size, enum memmap_types type)
|
|
{
|
|
if (memmap_nr >= ARRAY_SIZE(memmap_map)) {
|
|
pr_err("Ooops! Too many entries in the memory map!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (addr + size <= addr) {
|
|
pr_warn("Trying to add an invalid memory region, skipped\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memmap_map[memmap_nr].addr = addr;
|
|
memmap_map[memmap_nr].size = size;
|
|
memmap_map[memmap_nr].type = type;
|
|
memmap_nr++;
|
|
|
|
process_memmap();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct resource* __init
|
|
insert_ram_resource(u64 start, u64 end, bool reserved)
|
|
{
|
|
struct resource *res =
|
|
kzalloc(sizeof(struct resource), GFP_ATOMIC);
|
|
if (!res)
|
|
return NULL;
|
|
if (reserved) {
|
|
res->name = "reserved";
|
|
res->flags = IORESOURCE_MEM;
|
|
} else {
|
|
res->name = "System RAM";
|
|
res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
|
|
}
|
|
res->start = start;
|
|
res->end = end;
|
|
if (insert_resource(&iomem_resource, res)) {
|
|
kfree(res);
|
|
return NULL;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static int __init request_standard_resources(void)
|
|
{
|
|
struct memblock_region *mblk;
|
|
|
|
extern char _text[], _etext[];
|
|
extern char _sdata[], _edata[];
|
|
extern char __bss_start[], __bss_stop[];
|
|
|
|
for_each_mem_region(mblk) {
|
|
if (!memblock_is_nomap(mblk))
|
|
insert_ram_resource(mblk->base,
|
|
mblk->base + mblk->size - 1, 0);
|
|
else
|
|
insert_ram_resource(mblk->base,
|
|
mblk->base + mblk->size - 1, 1);
|
|
}
|
|
|
|
code_resource.start = __pa_symbol(_text);
|
|
code_resource.end = __pa_symbol(_etext)-1;
|
|
data_resource.start = __pa_symbol(_sdata);
|
|
data_resource.end = __pa_symbol(_edata)-1;
|
|
bss_resource.start = __pa_symbol(__bss_start);
|
|
bss_resource.end = __pa_symbol(__bss_stop)-1;
|
|
|
|
insert_resource(&iomem_resource, &code_resource);
|
|
insert_resource(&iomem_resource, &data_resource);
|
|
insert_resource(&iomem_resource, &bss_resource);
|
|
|
|
return 0;
|
|
}
|
|
subsys_initcall(request_standard_resources);
|
|
|
|
#ifdef CONFIG_NUMA
|
|
extern void cpu_set_node(void);
|
|
#endif
|
|
|
|
static void __init show_socket_mem_layout(void)
|
|
{
|
|
int i;
|
|
phys_addr_t base, size, end;
|
|
|
|
base = 0;
|
|
|
|
pr_info("Socket memory layout:\n");
|
|
for (i = 0; i < MAX_NUMSOCKETS; i++) {
|
|
if (socket_desc[i].is_online) {
|
|
size = socket_desc[i].socket_mem;
|
|
end = base + size - 1;
|
|
pr_info("Socket %d: [mem %#018llx-%#018llx], size %llu\n",
|
|
i, base, end, size);
|
|
base = end + 1;
|
|
}
|
|
}
|
|
pr_info("Reserved memory size for Socket 0: %#lx\n", NODE0_START);
|
|
}
|
|
|
|
int page_is_ram(unsigned long pfn)
|
|
{
|
|
pfn <<= PAGE_SHIFT;
|
|
|
|
return pfn >= mem_desc.base && pfn < (mem_desc.base + mem_desc.size);
|
|
}
|
|
|
|
static int __init topology_init(void)
|
|
{
|
|
int i;
|
|
|
|
#ifdef CONFIG_NUMA
|
|
for_each_online_node(i)
|
|
register_one_node(i);
|
|
#endif
|
|
|
|
for_each_possible_cpu(i) {
|
|
struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
return -ENOMEM;
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
if (i != 0)
|
|
p->hotpluggable = 1;
|
|
#endif
|
|
register_cpu(p, i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
subsys_initcall(topology_init);
|
|
|
|
static void __init setup_machine_fdt(void)
|
|
{
|
|
#ifdef CONFIG_USE_OF
|
|
void *dt_virt;
|
|
const char *name;
|
|
unsigned long phys_addr;
|
|
|
|
/* Give a chance to select kernel builtin DTB firstly */
|
|
if (IS_ENABLED(CONFIG_SW64_BUILTIN_DTB))
|
|
dt_virt = (void *)__dtb_start;
|
|
else
|
|
dt_virt = (void *)sunway_boot_params->dtb_start;
|
|
|
|
phys_addr = __phys_addr((unsigned long)dt_virt);
|
|
if (!phys_addr_valid(phys_addr) ||
|
|
!early_init_dt_scan(dt_virt)) {
|
|
pr_crit("\n"
|
|
"Error: invalid device tree blob at virtual address %px\n"
|
|
"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
|
|
"\nPlease check your bootloader.",
|
|
dt_virt);
|
|
|
|
while (true)
|
|
cpu_relax();
|
|
}
|
|
|
|
name = of_flat_dt_get_machine_name();
|
|
if (!name)
|
|
return;
|
|
|
|
pr_info("Machine model: %s\n", name);
|
|
#else
|
|
pr_info("Kernel disable device tree support.\n");
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
void __init device_tree_init(void)
|
|
{
|
|
unflatten_and_copy_device_tree();
|
|
sunway_boot_params->dtb_start = (__u64)initial_boot_params;
|
|
}
|
|
|
|
static void __init setup_cpu_info(void)
|
|
{
|
|
int i;
|
|
struct cache_desc *c;
|
|
unsigned long val;
|
|
|
|
val = cpuid(GET_TABLE_ENTRY, 0);
|
|
cpu_desc.model = CPUID_MODEL(val);
|
|
cpu_desc.family = CPUID_FAMILY(val);
|
|
cpu_desc.chip_var = CPUID_CHIP_VAR(val);
|
|
cpu_desc.arch_var = CPUID_ARCH_VAR(val);
|
|
cpu_desc.arch_rev = CPUID_ARCH_REV(val);
|
|
cpu_desc.pa_bits = CPUID_PA_BITS(val);
|
|
cpu_desc.va_bits = CPUID_VA_BITS(val);
|
|
|
|
if (*(unsigned long *)MMSIZE) {
|
|
static_branch_disable(&run_mode_host_key);
|
|
if (*(unsigned long *)MMSIZE & EMUL_FLAG) {
|
|
pr_info("run mode: emul\n");
|
|
static_branch_disable(&run_mode_guest_key);
|
|
static_branch_enable(&run_mode_emul_key);
|
|
|
|
} else {
|
|
pr_info("run mode: guest\n");
|
|
static_branch_enable(&run_mode_guest_key);
|
|
static_branch_disable(&run_mode_emul_key);
|
|
}
|
|
} else {
|
|
pr_info("run mode: host\n");
|
|
static_branch_enable(&run_mode_host_key);
|
|
static_branch_disable(&run_mode_guest_key);
|
|
static_branch_disable(&run_mode_emul_key);
|
|
}
|
|
|
|
for (i = 0; i < VENDOR_ID_MAX; i++) {
|
|
val = cpuid(GET_VENDOR_ID, i);
|
|
memcpy(cpu_desc.vendor_id + (i * 8), &val, 8);
|
|
}
|
|
|
|
for (i = 0; i < MODEL_MAX; i++) {
|
|
val = cpuid(GET_MODEL, i);
|
|
memcpy(cpu_desc.model_id + (i * 8), &val, 8);
|
|
}
|
|
|
|
cpu_desc.frequency = cpuid(GET_CPU_FREQ, 0) * 1000UL * 1000UL;
|
|
|
|
for (i = 0; i < NR_CPUS; i++) {
|
|
c = &(cpu_data[i].icache);
|
|
val = cpuid(GET_CACHE_INFO, L1_ICACHE);
|
|
c->size = CACHE_SIZE(val);
|
|
c->linesz = 1 << (CACHE_LINE_BITS(val));
|
|
c->sets = 1 << (CACHE_INDEX_BITS(val));
|
|
c->ways = c->size / c->sets / c->linesz;
|
|
|
|
c = &(cpu_data[i].dcache);
|
|
val = cpuid(GET_CACHE_INFO, L1_DCACHE);
|
|
c->size = CACHE_SIZE(val);
|
|
c->linesz = 1 << (CACHE_LINE_BITS(val));
|
|
c->sets = 1 << (CACHE_INDEX_BITS(val));
|
|
c->ways = c->size / c->sets / c->linesz;
|
|
|
|
c = &(cpu_data[i].scache);
|
|
val = cpuid(GET_CACHE_INFO, L2_CACHE);
|
|
c->size = CACHE_SIZE(val);
|
|
c->linesz = 1 << (CACHE_LINE_BITS(val));
|
|
c->sets = 1 << (CACHE_INDEX_BITS(val));
|
|
c->ways = c->size / c->sets / c->linesz;
|
|
|
|
c = &(cpu_data[i].tcache);
|
|
val = cpuid(GET_CACHE_INFO, L3_CACHE);
|
|
c->size = CACHE_SIZE(val);
|
|
c->linesz = 1 << (CACHE_LINE_BITS(val));
|
|
c->sets = 1 << (CACHE_INDEX_BITS(val));
|
|
c->ways = c->size / c->sets / c->linesz;
|
|
}
|
|
}
|
|
|
|
static void __init setup_socket_info(void)
|
|
{
|
|
int i;
|
|
int numsockets = sw64_chip->get_cpu_num();
|
|
|
|
memset(socket_desc, 0, MAX_NUMSOCKETS * sizeof(struct socket_desc_t));
|
|
|
|
for (i = 0; i < numsockets; i++) {
|
|
socket_desc[i].is_online = 1;
|
|
if (sw64_chip_init->early_init.get_node_mem)
|
|
socket_desc[i].socket_mem = sw64_chip_init->early_init.get_node_mem(i);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_BLK_DEV_INITRD
|
|
static void __init reserve_mem_for_initrd(void)
|
|
{
|
|
int ret;
|
|
|
|
initrd_start = sunway_boot_params->initrd_start;
|
|
if (initrd_start) {
|
|
initrd_start = __pa(initrd_start) + PAGE_OFFSET;
|
|
initrd_end = initrd_start + sunway_boot_params->initrd_size;
|
|
pr_info("Initial ramdisk at: 0x%px (%llu bytes)\n",
|
|
(void *)initrd_start, sunway_boot_params->initrd_size);
|
|
|
|
ret = add_memmap_region(__pa(initrd_start), initrd_end - initrd_start, memmap_initrd);
|
|
if (ret)
|
|
pr_err("Add initrd area [mem %#018lx-%#018lx] to memmap region failed.\n",
|
|
__pa(initrd_start), __pa(initrd_end - 1));
|
|
}
|
|
}
|
|
#endif /* CONFIG_BLK_DEV_INITRD */
|
|
|
|
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
|
|
static int __init early_kvm_reserved_mem(char *p)
|
|
{
|
|
if (!p) {
|
|
pr_err("Config string not provided\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
kvm_mem_size = memparse(p, &p);
|
|
if (*p != '@')
|
|
return -EINVAL;
|
|
kvm_mem_base = memparse(p + 1, &p);
|
|
return 0;
|
|
}
|
|
early_param("kvm_mem", early_kvm_reserved_mem);
|
|
|
|
void __init sw64_kvm_reserve(void)
|
|
{
|
|
kvm_cma_declare_contiguous(kvm_mem_base, kvm_mem_size, 0,
|
|
PAGE_SIZE, 0, "sw64_kvm_cma", &sw64_kvm_cma);
|
|
}
|
|
#endif
|
|
|
|
void __init
|
|
setup_arch(char **cmdline_p)
|
|
{
|
|
jump_label_init();
|
|
setup_cpu_info();
|
|
sw64_chip->fixup();
|
|
sw64_chip_init->fixup();
|
|
setup_socket_info();
|
|
show_socket_mem_layout();
|
|
sw64_chip_init->early_init.setup_core_start(&core_start);
|
|
|
|
setup_sched_clock();
|
|
#ifdef CONFIG_GENERIC_SCHED_CLOCK
|
|
sw64_sched_clock_init();
|
|
#endif
|
|
|
|
setup_machine_fdt();
|
|
|
|
/* Register a call for panic conditions. */
|
|
atomic_notifier_chain_register(&panic_notifier_list,
|
|
&sw64_panic_block);
|
|
|
|
callback_init();
|
|
|
|
/* command line */
|
|
if (!sunway_boot_params->cmdline)
|
|
sunway_boot_params->cmdline = (unsigned long)COMMAND_LINE;
|
|
|
|
strlcpy(boot_command_line, (char *)sunway_boot_params->cmdline, COMMAND_LINE_SIZE);
|
|
|
|
#if IS_ENABLED(CONFIG_CMDLINE_BOOL)
|
|
#if IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)
|
|
strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
|
|
strlcpy((char *)sunway_boot_params->cmdline, boot_command_line, COMMAND_LINE_SIZE);
|
|
#else
|
|
if (builtin_cmdline[0]) {
|
|
/* append builtin to boot loader cmdline */
|
|
strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
|
|
strlcat(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
|
|
}
|
|
#endif /* CMDLINE_EXTEND */
|
|
#endif
|
|
if (IS_ENABLED(CONFIG_SW64_CHIP3_ASIC_DEBUG) &&
|
|
IS_ENABLED(CONFIG_SW64_CHIP3)) {
|
|
unsigned long bmc, cpu_online, node;
|
|
|
|
bmc = *(unsigned long *)__va(0x800000);
|
|
pr_info("bmc = %ld\n", bmc);
|
|
cpu_online = sw64_chip->get_cpu_num();
|
|
for (node = 0; node < cpu_online; node++)
|
|
sw64_io_write(node, SI_FAULT_INT_EN, 0);
|
|
sprintf(boot_command_line, "root=/dev/sda2 ip=172.16.137.%ld::172.16.137.254:255.255.255.0::eth0:off", 180+bmc);
|
|
}
|
|
|
|
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
|
|
*cmdline_p = command_line;
|
|
|
|
/*
|
|
* Process command-line arguments.
|
|
*/
|
|
parse_early_param();
|
|
|
|
/* Find our memory. */
|
|
mem_detect();
|
|
|
|
#ifdef CONFIG_PCI
|
|
reserve_mem_for_pci();
|
|
#endif
|
|
|
|
#ifdef CONFIG_BLK_DEV_INITRD
|
|
reserve_mem_for_initrd();
|
|
#endif
|
|
|
|
sw64_memblock_init();
|
|
|
|
/* Reserve large chunks of memory for use by CMA for KVM. */
|
|
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
|
|
sw64_kvm_reserve();
|
|
#endif
|
|
|
|
sw64_numa_init();
|
|
|
|
memblock_dump_all();
|
|
|
|
sparse_init();
|
|
|
|
zone_sizes_init();
|
|
|
|
paging_init();
|
|
|
|
kexec_control_page_init();
|
|
|
|
efi_init();
|
|
|
|
/* Parse the ACPI tables for possible boot-time configuration */
|
|
acpi_boot_table_init();
|
|
|
|
/*
|
|
* Initialize the machine. Usually has to do with setting up
|
|
* DMA windows and the like.
|
|
*/
|
|
sw64_init_arch();
|
|
|
|
reserve_crashkernel();
|
|
/* Reserve standard resources. */
|
|
reserve_std_resources();
|
|
|
|
/*
|
|
* Give us a default console. TGA users will see nothing until
|
|
* chr_dev_init is called, rather late in the boot sequence.
|
|
*/
|
|
|
|
#ifdef CONFIG_VT
|
|
#if defined(CONFIG_VGA_CONSOLE)
|
|
conswitchp = &vga_con;
|
|
#elif defined(CONFIG_DUMMY_CONSOLE)
|
|
conswitchp = &dummy_con;
|
|
#endif
|
|
#endif
|
|
|
|
/* Default root filesystem to sda2. */
|
|
ROOT_DEV = Root_SDA2;
|
|
|
|
/*
|
|
* Identify the flock of penguins.
|
|
*/
|
|
|
|
#ifdef CONFIG_SMP
|
|
setup_smp();
|
|
#endif
|
|
#ifdef CONFIG_NUMA
|
|
cpu_set_node();
|
|
#endif
|
|
if (acpi_disabled)
|
|
device_tree_init();
|
|
}
|
|
|
|
|
|
static int
|
|
show_cpuinfo(struct seq_file *f, void *slot)
|
|
{
|
|
int i;
|
|
unsigned long cpu_freq;
|
|
|
|
cpu_freq = get_cpu_freq() / 1000 / 1000;
|
|
|
|
for_each_online_cpu(i) {
|
|
/*
|
|
* glibc reads /proc/cpuinfo to determine the number of
|
|
* online processors, looking for lines beginning with
|
|
* "processor". Give glibc what it expects.
|
|
*/
|
|
seq_printf(f, "processor\t: %u\n"
|
|
"vendor_id\t: %s\n"
|
|
"cpu family\t: %d\n"
|
|
"model\t\t: %u\n"
|
|
"model name\t: %s CPU @ %lu.%lu%luGHz\n"
|
|
"cpu variation\t: %u\n"
|
|
"cpu revision\t: %u\n",
|
|
i, cpu_desc.vendor_id, cpu_desc.family,
|
|
cpu_desc.model, cpu_desc.model_id,
|
|
cpu_freq / 1000, (cpu_freq % 1000) / 100,
|
|
(cpu_freq % 100) / 10,
|
|
cpu_desc.arch_var, cpu_desc.arch_rev);
|
|
seq_printf(f, "cpu MHz\t\t: %lu.00\n"
|
|
"cache size\t: %u KB\n"
|
|
"physical id\t: %d\n"
|
|
"bogomips\t: %lu.%02lu\n",
|
|
cpu_freq, cpu_data[i].tcache.size >> 10,
|
|
cpu_to_rcid(i),
|
|
loops_per_jiffy / (500000/HZ),
|
|
(loops_per_jiffy / (5000/HZ)) % 100);
|
|
|
|
seq_printf(f, "flags\t\t: fpu simd vpn upn cpuid\n");
|
|
seq_printf(f, "page size\t: %d\n", 8192);
|
|
seq_printf(f, "cache_alignment\t: %d\n", cpu_data[i].tcache.linesz);
|
|
seq_printf(f, "address sizes\t: %u bits physical, %u bits virtual\n\n",
|
|
cpu_desc.pa_bits, cpu_desc.va_bits);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We show only CPU #0 info.
|
|
*/
|
|
static void *
|
|
c_start(struct seq_file *f, loff_t *pos)
|
|
{
|
|
return *pos < 1 ? (void *)1 : NULL;
|
|
}
|
|
|
|
static void *
|
|
c_next(struct seq_file *f, void *v, loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
c_stop(struct seq_file *f, void *v)
|
|
{
|
|
}
|
|
|
|
const struct seq_operations cpuinfo_op = {
|
|
.start = c_start,
|
|
.next = c_next,
|
|
.stop = c_stop,
|
|
.show = show_cpuinfo,
|
|
};
|
|
|
|
|
|
static int
|
|
sw64_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
{
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static __init int add_pcspkr(void)
|
|
{
|
|
struct platform_device *pd;
|
|
int ret;
|
|
|
|
pd = platform_device_alloc("pcspkr", -1);
|
|
if (!pd)
|
|
return -ENOMEM;
|
|
|
|
ret = platform_device_add(pd);
|
|
if (ret)
|
|
platform_device_put(pd);
|
|
|
|
return ret;
|
|
}
|
|
device_initcall(add_pcspkr);
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
struct dentry *sw64_debugfs_dir;
|
|
static int __init debugfs_sw64(void)
|
|
{
|
|
struct dentry *d;
|
|
|
|
d = debugfs_create_dir("sw_64", NULL);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
sw64_debugfs_dir = d;
|
|
return 0;
|
|
}
|
|
arch_initcall(debugfs_sw64);
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
static int __init sw64_of_init(void)
|
|
{
|
|
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
|
return 0;
|
|
}
|
|
core_initcall(sw64_of_init);
|
|
#endif
|
|
|
|
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
|
|
static int __init sw64_kvm_pool_init(void)
|
|
{
|
|
int status = 0;
|
|
unsigned long kvm_pool_virt;
|
|
struct page *base_page, *end_page, *p;
|
|
|
|
if (!sw64_kvm_cma)
|
|
goto out;
|
|
|
|
kvm_pool_virt = (unsigned long)kvm_mem_base;
|
|
|
|
sw64_kvm_pool = gen_pool_create(PAGE_SHIFT, -1);
|
|
if (!sw64_kvm_pool)
|
|
goto out;
|
|
|
|
status = gen_pool_add_virt(sw64_kvm_pool, kvm_pool_virt, kvm_mem_base,
|
|
kvm_mem_size, -1);
|
|
if (status < 0) {
|
|
pr_err("failed to add memory chunks to sw64 kvm pool\n");
|
|
gen_pool_destroy(sw64_kvm_pool);
|
|
sw64_kvm_pool = NULL;
|
|
goto out;
|
|
}
|
|
gen_pool_set_algo(sw64_kvm_pool, gen_pool_best_fit, NULL);
|
|
|
|
base_page = pfn_to_page(kvm_mem_base >> PAGE_SHIFT);
|
|
end_page = pfn_to_page((kvm_mem_base + kvm_mem_size - 1) >> PAGE_SHIFT);
|
|
|
|
p = base_page;
|
|
while (page_ref_count(p) == 0 &&
|
|
(unsigned long)p <= (unsigned long)end_page) {
|
|
set_page_count(p, 1);
|
|
page_mapcount_reset(p);
|
|
SetPageReserved(p);
|
|
p++;
|
|
}
|
|
|
|
return status;
|
|
|
|
out:
|
|
return -ENOMEM;
|
|
}
|
|
core_initcall_sync(sw64_kvm_pool_init);
|
|
#endif
|