diff --git a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi index 74fcd0a4f720..ea1a9f537f75 100644 --- a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi @@ -282,6 +282,7 @@ status = "okay"; reg = <0xFF6345E0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm/boot/dts/amlogic/mesong12a.dtsi b/arch/arm/boot/dts/amlogic/mesong12a.dtsi index 9fe67ed95516..0a37c434eac1 100644 --- a/arch/arm/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12a.dtsi @@ -445,6 +445,7 @@ status = "okay"; reg = <0xFF6345E0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm/boot/dts/amlogic/mesong12b.dtsi b/arch/arm/boot/dts/amlogic/mesong12b.dtsi index 331e5cd83810..fd1b61841a0f 100644 --- a/arch/arm/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12b.dtsi @@ -550,6 +550,14 @@ #thermal-sensor-cells = <1>; }; + ram-dump { + compatible = "amlogic, ram_dump"; + status = "okay"; + reg = <0xFF6345E0 4>; + reg-names = "PREG_STICKY_REG8"; + store_device = "data"; + }; + soc { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/amlogic/mesongxl.dtsi b/arch/arm/boot/dts/amlogic/mesongxl.dtsi index b099b579a7ec..090955128852 100644 --- a/arch/arm/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxl.dtsi @@ -247,6 +247,7 @@ status = "okay"; reg = <0xC88345E0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm/boot/dts/amlogic/mesongxm.dtsi b/arch/arm/boot/dts/amlogic/mesongxm.dtsi index a6156ce0d406..e18977ab6068 100644 --- a/arch/arm/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxm.dtsi @@ -282,6 +282,7 @@ status = "okay"; reg = <0xC88345E0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi index 56e8a9552d3b..d8be5b4883d6 100644 --- a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi @@ -285,6 +285,7 @@ status = "okay"; reg = <0xFF6345E0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index b3cd3667c5ce..42be203d849b 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -287,6 +287,7 @@ status = "okay"; reg = <0x0 0xFF6345E0 0x0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 808870ba55e8..35bdc568ec1a 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -445,6 +445,7 @@ status = "okay"; reg = <0x0 0xFF6345E0 0x0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi index 32aa9ff49731..1a6f3015969e 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi @@ -550,6 +550,14 @@ #thermal-sensor-cells = <1>; }; + ram-dump { + compatible = "amlogic, ram_dump"; + status = "okay"; + reg = <0x0 0xFF6345E0 0x0 4>; + reg-names = "PREG_STICKY_REG8"; + store_device = "data"; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 389db76b9312..db830e0c7538 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -234,6 +234,7 @@ status = "okay"; reg = <0x0 0xC88345E0 0x0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 049541f6b3e1..e16d2a7df116 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -282,6 +282,7 @@ status = "okay"; reg = <0x0 0xC88345E0 0x0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi index 7306feb176f3..a09d0dff6aa0 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi @@ -285,6 +285,7 @@ status = "okay"; reg = <0x0 0xFF6345E0 0x0 4>; reg-names = "PREG_STICKY_REG8"; + store_device = "data"; }; jtag { diff --git a/drivers/amlogic/memory_ext/ram_dump.c b/drivers/amlogic/memory_ext/ram_dump.c index c18808850b62..5290a82aaeb1 100644 --- a/drivers/amlogic/memory_ext/ram_dump.c +++ b/drivers/amlogic/memory_ext/ram_dump.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,32 +34,31 @@ #include #include #include +#include +#include +#include +#include +#include #include static unsigned long ramdump_base __initdata; static unsigned long ramdump_size __initdata; static bool ramdump_disable __initdata; +#define WAIT_TIMEOUT (40ULL * 1000 * 1000 * 1000) + struct ramdump { unsigned long mem_base; unsigned long mem_size; - struct mutex lock; - struct kobject *kobj; - struct work_struct clear_work; + unsigned long long tick; + void *mnt_buf; + const char *storage_device; + struct delayed_work work; int disable; }; static struct ramdump *ram; -static void meson_set_reboot_reason(int reboot_reason) -{ - struct arm_smccc_res smccc; - - arm_smccc_smc(SET_REBOOT_REASON, - reboot_reason, 0, 0, 0, 0, 0, 0, &smccc); - return; -} - static int __init early_ramdump_para(char *buf) { int ret; @@ -85,94 +85,10 @@ static int __init early_ramdump_para(char *buf) ramdump_disable = 1; } } - if (ramdump_disable) - meson_set_reboot_reason(MESON_NORMAL_BOOT); - return 0; } early_param("ramdump", early_ramdump_para); -static ssize_t ramdump_bin_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - void *p = NULL; -#ifndef CONFIG_64BIT - struct page *page, *pages[1]; -#endif - - if (!ram->mem_base || off >= ram->mem_size) - return 0; - -#ifndef CONFIG_64BIT - page = phys_to_page(ram->mem_base + off); - pages[0] = page; - p = vmap(pages, 1, VM_MAP, PAGE_KERNEL); - if (!p) { - pr_info("%s, map %lx, %d failed, page:%p, pfn:%lx\n", - __func__, (unsigned long)(ram->mem_base + off), - count, page, page_to_pfn(page)); - return -EINVAL; - } -#else - p = (void *)(ram->mem_base + off); -#endif - if (off + count > ram->mem_size) - count = ram->mem_size - off; - mutex_lock(&ram->lock); - memcpy(buf, p, count); - mutex_unlock(&ram->lock); - - /* debug when read end */ - if (off + count >= ram->mem_size) - pr_info("%s, p=%p %p, off:%lli, c:%zi\n", - __func__, buf, p, off, count); -#ifndef CONFIG_64BIT - vunmap(p); -#endif - return count; -} - -int ramdump_disabled(void) -{ - if (ram) - return ram->disable; - return 0; -} -EXPORT_SYMBOL(ramdump_disabled); - -static ssize_t ramdump_bin_write(struct file *filp, - struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - if (!ram->mem_size) - return -EINVAL; - - if (ram->mem_base && !strncmp("reboot", buf, 6)) - kernel_restart("RAM-DUMP finished\n"); - - if (!strncmp("disable", buf, 7)) { - ram->disable = 1; - meson_set_reboot_reason(MESON_NORMAL_BOOT); - } - if (!strncmp("enable", buf, 6)) { - ram->disable = 0; - meson_set_reboot_reason(MESON_KERNEL_PANIC); - } - - return count; -} - -static struct bin_attribute ramdump_attr = { - .attr = { - .name = "compmsg", - .mode = S_IRUGO | S_IWUSR, - }, - .read = ramdump_bin_read, - .write = ramdump_bin_write, -}; - /* * clear memory to avoid large amount of memory not used. * for ramdom data, it's hard to compress @@ -280,6 +196,198 @@ void ramdump_sync_data(void) } #endif +#ifdef CONFIG_64BIT +static void free_reserved_highmem(unsigned long start, unsigned long end) +{ +} +#else +static void free_reserved_highmem(unsigned long start, unsigned long end) +{ + for (; start < end; ) { + free_highmem_page(phys_to_page(start)); + start += PAGE_SIZE; + } +} +#endif + +static void free_reserved_mem(unsigned long start, unsigned long size) +{ + unsigned long end = PAGE_ALIGN(start + size); + struct page *page, *epage; + + page = phys_to_page(start); + if (PageHighMem(page)) { + free_reserved_highmem(start, end); + } else { + epage = phys_to_page(end); + if (!PageHighMem(epage)) { + free_reserved_area(__va(start), + __va(end), 0, "ramdump"); + } else { + /* reserved area cross zone */ + struct zone *zone; + unsigned long bound; + + zone = page_zone(page); + bound = zone_end_pfn(zone); + free_reserved_area(__va(start), + __va(bound << PAGE_SHIFT), + 0, "ramdump"); + zone = page_zone(epage); + bound = zone->zone_start_pfn; + free_reserved_highmem(bound << PAGE_SHIFT, end); + } + } +} + +static int check_storage_mounted(char **root) +{ + int fd, cnt, ret = 0; + char mnt_dev[64] = {}, *mnt_ptr, *root_dir; + + fd = sys_open("/proc/mounts", O_RDONLY, 0); + if (!fd) { + pr_debug("%s, open mounts failed:%d\n", __func__, fd); + return -EINVAL; + } + cnt = sys_read(fd, ram->mnt_buf, PAGE_SIZE); + if (cnt < 0) { + pr_debug("%s, read mounts failed:%d\n", __func__, cnt); + ret = -ENODEV; + goto exit; + } + pr_debug("read:%d, %s\n", cnt, (char *)ram->mnt_buf); + sprintf(mnt_dev, "/dev/block/%s", ram->storage_device); + mnt_ptr = strstr((char *)ram->mnt_buf, mnt_dev); + if (mnt_ptr) { + pr_debug("%s, find %s in buffer, ptr:%p\n", + __func__, mnt_dev, mnt_ptr); + root_dir = strstr(mnt_ptr, " "); + root_dir++; + *root = root_dir; + pr_debug("mount:%s root:%s\n", mnt_ptr, root_dir); + } else + ret = -ENODEV; +exit: + sys_close(fd); + return ret; +} + +static size_t save_data(int fd) +{ + unsigned long saved = 0, off = 0, s = 0, e; + void *buffer; + int block = (1 << (PAGE_SHIFT + MAX_ORDER - 1)), wsize = 0, ret, i; + struct vm_struct *area; + struct page *page, **pages = NULL; + + area = get_vm_area(block, VM_ALLOC); + if (!area) { + pr_err("%s, get vma failed\n", __func__); + return -ENOMEM; + } + + pages = kzalloc(sizeof(unsigned long) * MAX_ORDER_NR_PAGES, GFP_KERNEL); + if (!pages) + goto out; + + buffer = area->addr; + while (saved < ram->mem_size) { + s = ram->mem_size - saved; + if (s >= block) + wsize = block; + else + wsize = s; + + s = ram->mem_base + off; + e = s + PAGE_ALIGN(wsize); + + page = phys_to_page(s); + for (i = 0; i < MAX_ORDER_NR_PAGES; i++) { + pages[i] = page; + page++; + } + ret = map_kernel_range_noflush((unsigned long)buffer, + PAGE_ALIGN(wsize), + PAGE_KERNEL, + pages); + if (!ret) { + pr_err("map page:%lx failed\n", page_to_pfn(page)); + goto out; + } + ret = sys_write(fd, buffer, wsize); + if (ret != wsize) { + unmap_kernel_range((unsigned long)buffer, + PAGE_ALIGN(wsize)); + pr_err("%s, write failed\n", __func__); + goto out; + } + unmap_kernel_range((unsigned long)buffer, PAGE_ALIGN(wsize)); + free_reserved_mem(s, PAGE_ALIGN(wsize)); + saved += wsize; + off += wsize; + pr_debug("%s, write %08lx, size:%08x, saved:%08lx, off:%lx\n", + __func__, s, wsize, saved, off); + } +out: + free_vm_area(area); + kfree(pages); + pr_info("%s, write %08lx, size:%08x, saved:%08lx, off:%lx\n", + __func__, s, wsize, saved, off); + return saved; +} + +#define OPEN_FLAGS (O_WRONLY | O_CREAT | O_DSYNC | O_TRUNC) + +static void wait_to_save(struct work_struct *work) +{ + char *root; + int fd, ret = 0; + int need_reboot = 0; + + if ((sched_clock() - ram->tick) >= WAIT_TIMEOUT) { + pr_err("can't find mounted device, free saved data\n"); + need_reboot = 3; + goto exit; + } + + if (!check_storage_mounted(&root)) { + char wname[64] = {}, *next_token; + + /* write compressed data to storage device */ + next_token = strstr(root, " "); + if (next_token) + *next_token = '\0'; + sprintf(wname, "%s/crashdump-1.bin", root); + fd = sys_open(wname, OPEN_FLAGS, 0644); + if (fd < 0) { + pr_info("open %s failed:%d\n", wname, fd); + need_reboot = 3; + goto exit; + } + ret = save_data(fd); + if (ret != ram->mem_size) { + pr_err("write size %d not match %ld\n", + ret, ram->mem_size); + } + sys_fsync(fd); + sys_close(fd); + sys_sync(); + need_reboot = 1; + } else + schedule_delayed_work(&ram->work, 500); + +exit: + /* Nomatter what happened, reboot must be done in this function */ + if (need_reboot) { + if (need_reboot & 0x1) + kfree(ram->mnt_buf); + if (need_reboot & 0x02) + free_reserved_mem(ram->mem_base, ram->mem_size); + kernel_restart("RAM-DUMP finished"); + } +} + static int __init ramdump_probe(struct platform_device *pdev) { void __iomem *p = NULL; @@ -289,6 +397,7 @@ static int __init ramdump_probe(struct platform_device *pdev) unsigned int dump_set; int ret; void __iomem *base; + const char *dev_name = NULL; np = of_find_node_by_name(NULL, "memory"); if (!np) @@ -331,35 +440,33 @@ static int __init ramdump_probe(struct platform_device *pdev) if (ramdump_disable) ram->disable = 1; + np = pdev->dev.of_node; + ret = of_property_read_string(np, "store_device", &dev_name); + if (!ret) { + ram->storage_device = dev_name; + pr_info("%s, storage device:%s\n", __func__, dev_name); + } + if (!ramdump_base || !ramdump_size) { pr_info("NO valid ramdump args:%lx %lx\n", ramdump_base, ramdump_size); } else { - #ifdef CONFIG_64BIT - p = ioremap_cache(ramdump_base, ramdump_size); - ram->mem_base = (unsigned long)p; - ram->mem_size = ramdump_size; - #else ram->mem_base = ramdump_base; ram->mem_size = ramdump_size; - #endif pr_info("%s, mem_base:%p, %lx, size:%lx\n", __func__, p, ramdump_base, ramdump_size); } - ram->kobj = kobject_create_and_add("mdump", kernel_kobj); - if (!ram->kobj) { - pr_err("%s, create sysfs failed\n", __func__); - goto err; - } - ramdump_attr.size = ram->mem_size; - if (sysfs_create_bin_file(ram->kobj, &ramdump_attr)) { - pr_err("%s, create sysfs1 failed\n", __func__); - goto err1; - } - mutex_init(&ram->lock); - if (!ram->disable && !ram->mem_size) { - INIT_WORK(&ram->clear_work, lazy_clear_work); - schedule_work(&ram->clear_work); + + if (!ram->disable) { + if (!ram->mem_base) { /* No compressed data */ + INIT_DELAYED_WORK(&ram->work, lazy_clear_work); + } else { /* with compressed data */ + INIT_DELAYED_WORK(&ram->work, wait_to_save); + ram->tick = sched_clock(); + ram->mnt_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + WARN_ON(!ram->mnt_buf); + } + schedule_delayed_work(&ram->work, 1); } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "PREG_STICKY_REG8"); @@ -378,12 +485,7 @@ static int __init ramdump_probe(struct platform_device *pdev) } return 0; -err1: - kobject_put(ram->kobj); err: -#ifdef CONFIG_64BIT - iounmap((void *)ram->mem_base); -#endif kfree(ram); return -EINVAL; @@ -391,11 +493,6 @@ err: static int ramdump_remove(struct platform_device *pdev) { - sysfs_remove_bin_file(ram->kobj, &ramdump_attr); -#ifdef CONFIG_64BIT - iounmap((void *)ram->mem_base); -#endif - kobject_put(ram->kobj); kfree(ram); return 0; } diff --git a/drivers/amlogic/reboot/reboot.c b/drivers/amlogic/reboot/reboot.c index 3545f43f389e..d520cf36a9de 100644 --- a/drivers/amlogic/reboot/reboot.c +++ b/drivers/amlogic/reboot/reboot.c @@ -33,10 +33,6 @@ #include #include #include -#ifdef CONFIG_AMLOGIC_RAMDUMP -#include -#define RAMDUMP_REPLACE_MSG "ramdump disabled, replase panic to normal\n" -#endif /* CONFIG_AMLOGIC_RAMDUMP */ #if defined(CONFIG_ARCH_MESON64_ODROID_COMMON) #include @@ -89,15 +85,7 @@ static u32 parse_reason(const char *cmd) } else { if (kernel_panic) { if (strcmp(kernel_panic, "kernel_panic") == 0) { - #ifdef CONFIG_AMLOGIC_RAMDUMP - if (ramdump_disabled()) { - reboot_reason = MESON_NORMAL_BOOT; - pr_info(RAMDUMP_REPLACE_MSG); - } else - reboot_reason = MESON_KERNEL_PANIC; - #else reboot_reason = MESON_KERNEL_PANIC; - #endif } } diff --git a/include/linux/amlogic/ramdump.h b/include/linux/amlogic/ramdump.h index 7944e6578245..8782418c1113 100644 --- a/include/linux/amlogic/ramdump.h +++ b/include/linux/amlogic/ramdump.h @@ -23,7 +23,6 @@ #define AMLOGIC_KERNEL_BOOTED 0x8000 #define RAMDUMP_STICKY_DATA_MASK 0xFFFF -extern int ramdump_disabled(void); extern void ramdump_sync_data(void); #endif /* __RAMDUMP_H__ */