Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

remoteproc: imx_dsp_rproc: Add custom memory copy implementation for i.MX DSP Cores

The IRAM is part of the HiFi DSP.
According to hardware specification only 32-bits write are allowed
otherwise we get a Kernel panic.

Therefore add a custom memory copy and memset functions to deal with
the above restriction.

Signed-off-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Link: https://lore.kernel.org/r/20230221170356.27923-1-iuliana.prodan@oss.nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

authored by

Iuliana Prodan and committed by
Mathieu Poirier
408ec1ff 11bb42a9

+186 -1
+186 -1
drivers/remoteproc/imx_dsp_rproc.c
··· 738 738 dev_err(dev, "%s: failed (%d, err:%d)\n", __func__, vqid, err); 739 739 } 740 740 741 + /* 742 + * Custom memory copy implementation for i.MX DSP Cores 743 + * 744 + * The IRAM is part of the HiFi DSP. 745 + * According to hw specs only 32-bits writes are allowed. 746 + */ 747 + static int imx_dsp_rproc_memcpy(void *dest, const void *src, size_t size) 748 + { 749 + const u8 *src_byte = src; 750 + const u32 *source = src; 751 + u32 affected_mask; 752 + u32 *dst = dest; 753 + int i, q, r; 754 + u32 tmp; 755 + 756 + /* destination must be 32bit aligned */ 757 + if (!IS_ALIGNED((uintptr_t)dest, 4)) 758 + return -EINVAL; 759 + 760 + q = size / 4; 761 + r = size % 4; 762 + 763 + /* copy data in units of 32 bits at a time */ 764 + for (i = 0; i < q; i++) 765 + writel(source[i], &dst[i]); 766 + 767 + if (r) { 768 + affected_mask = GENMASK(8 * r, 0); 769 + 770 + /* 771 + * first read the 32bit data of dest, then change affected 772 + * bytes, and write back to dest. 773 + * For unaffected bytes, it should not be changed 774 + */ 775 + tmp = readl(dest + q * 4); 776 + tmp &= ~affected_mask; 777 + 778 + /* avoid reading after end of source */ 779 + for (i = 0; i < r; i++) 780 + tmp |= (src_byte[q * 4 + i] << (8 * i)); 781 + 782 + writel(tmp, dest + q * 4); 783 + } 784 + 785 + return 0; 786 + } 787 + 788 + /* 789 + * Custom memset implementation for i.MX DSP Cores 790 + * 791 + * The IRAM is part of the HiFi DSP. 792 + * According to hw specs only 32-bits writes are allowed. 793 + */ 794 + static int imx_dsp_rproc_memset(void *addr, u8 value, size_t size) 795 + { 796 + u32 tmp_val = value; 797 + u32 *tmp_dst = addr; 798 + u32 affected_mask; 799 + int q, r; 800 + u32 tmp; 801 + 802 + /* destination must be 32bit aligned */ 803 + if (!IS_ALIGNED((uintptr_t)addr, 4)) 804 + return -EINVAL; 805 + 806 + tmp_val |= tmp_val << 8; 807 + tmp_val |= tmp_val << 16; 808 + 809 + q = size / 4; 810 + r = size % 4; 811 + 812 + while (q--) 813 + writel(tmp_val, tmp_dst++); 814 + 815 + if (r) { 816 + affected_mask = GENMASK(8 * r, 0); 817 + 818 + /* 819 + * first read the 32bit data of addr, then change affected 820 + * bytes, and write back to addr. 821 + * For unaffected bytes, it should not be changed 822 + */ 823 + tmp = readl(tmp_dst); 824 + tmp &= ~affected_mask; 825 + 826 + tmp |= (tmp_val & affected_mask); 827 + writel(tmp, tmp_dst); 828 + } 829 + 830 + return 0; 831 + } 832 + 833 + /* 834 + * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory 835 + * @rproc: remote processor which will be booted using these fw segments 836 + * @fw: the ELF firmware image 837 + * 838 + * This function loads the firmware segments to memory, where the remote 839 + * processor expects them. 840 + * 841 + * Return: 0 on success and an appropriate error code otherwise 842 + */ 843 + static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) 844 + { 845 + struct device *dev = &rproc->dev; 846 + const void *ehdr, *phdr; 847 + int i, ret = 0; 848 + u16 phnum; 849 + const u8 *elf_data = fw->data; 850 + u8 class = fw_elf_get_class(fw); 851 + u32 elf_phdr_get_size = elf_size_of_phdr(class); 852 + 853 + ehdr = elf_data; 854 + phnum = elf_hdr_get_e_phnum(class, ehdr); 855 + phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr); 856 + 857 + /* go through the available ELF segments */ 858 + for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) { 859 + u64 da = elf_phdr_get_p_paddr(class, phdr); 860 + u64 memsz = elf_phdr_get_p_memsz(class, phdr); 861 + u64 filesz = elf_phdr_get_p_filesz(class, phdr); 862 + u64 offset = elf_phdr_get_p_offset(class, phdr); 863 + u32 type = elf_phdr_get_p_type(class, phdr); 864 + void *ptr; 865 + 866 + if (type != PT_LOAD || !memsz) 867 + continue; 868 + 869 + dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", 870 + type, da, memsz, filesz); 871 + 872 + if (filesz > memsz) { 873 + dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n", 874 + filesz, memsz); 875 + ret = -EINVAL; 876 + break; 877 + } 878 + 879 + if (offset + filesz > fw->size) { 880 + dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n", 881 + offset + filesz, fw->size); 882 + ret = -EINVAL; 883 + break; 884 + } 885 + 886 + if (!rproc_u64_fit_in_size_t(memsz)) { 887 + dev_err(dev, "size (%llx) does not fit in size_t type\n", 888 + memsz); 889 + ret = -EOVERFLOW; 890 + break; 891 + } 892 + 893 + /* grab the kernel address for this device address */ 894 + ptr = rproc_da_to_va(rproc, da, memsz, NULL); 895 + if (!ptr) { 896 + dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da, 897 + memsz); 898 + ret = -EINVAL; 899 + break; 900 + } 901 + 902 + /* put the segment where the remote processor expects it */ 903 + if (filesz) { 904 + ret = imx_dsp_rproc_memcpy(ptr, elf_data + offset, filesz); 905 + if (ret) { 906 + dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%llx\n", 907 + da, memsz); 908 + break; 909 + } 910 + } 911 + 912 + /* zero out remaining memory for this segment */ 913 + if (memsz > filesz) { 914 + ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz); 915 + if (ret) { 916 + dev_err(dev, "memset failed for da 0x%llx memsz 0x%llx\n", 917 + da, memsz); 918 + break; 919 + } 920 + } 921 + } 922 + 923 + return ret; 924 + } 925 + 741 926 static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) 742 927 { 743 928 if (rproc_elf_load_rsc_table(rproc, fw)) ··· 937 752 .start = imx_dsp_rproc_start, 938 753 .stop = imx_dsp_rproc_stop, 939 754 .kick = imx_dsp_rproc_kick, 940 - .load = rproc_elf_load_segments, 755 + .load = imx_dsp_rproc_elf_load_segments, 941 756 .parse_fw = imx_dsp_rproc_parse_fw, 942 757 .sanity_check = rproc_elf_sanity_check, 943 758 .get_boot_addr = rproc_elf_get_boot_addr,