summaryrefslogtreecommitdiff
path: root/ldso/ldso/bfin/dl-inlines.h
blob: b08ce61cbc1a9d65b7b7ed94ebd46df287cc8ebb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Copyright (C) 2003, 2004 Red Hat, Inc.
 * Contributed by Alexandre Oliva <aoliva@redhat.com>
 * Copyright (C) 2006-2011 Analog Devices, Inc.
 *
 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
 */

#include <bfin_sram.h>

#define __dl_loadaddr_unmap __dl_loadaddr_unmap

#include "../fdpic/dl-inlines.h"

static __always_inline void
__dl_loadaddr_unmap(struct elf32_fdpic_loadaddr loadaddr,
                    struct funcdesc_ht *funcdesc_ht)
{
	int i;

	for (i = 0; i < loadaddr.map->nsegs; i++) {
		struct elf32_fdpic_loadseg *segdata;
		ssize_t offs;
		segdata = loadaddr.map->segs + i;

		/* FIXME:
		 * A more cleaner way is to add type for struct elf32_fdpic_loadseg,
		 * and release the memory according to the type.
		 * Currently, we hardcode the memory address of L1 SRAM.
		 */
		if ((segdata->addr & 0xff800000) == 0xff800000) {
			_dl_sram_free((void *)segdata->addr);
			continue;
		}

		offs = (segdata->p_vaddr & ADDR_ALIGN);
		_dl_munmap((void*)segdata->addr - offs,
			segdata->p_memsz + offs);
	  }

	/*
	 * _dl_unmap is only called for dlopen()ed libraries, for which
	 * calling free() is safe, or before we've completed the initial
	 * relocation, in which case calling free() is probably pointless,
	 * but still safe.
	 */
	_dl_free(loadaddr.map);
	if (funcdesc_ht)
		htab_delete(funcdesc_ht);
}

static __always_inline int
__dl_is_special_segment(Elf32_Ehdr *epnt, Elf32_Phdr *ppnt)
{
	if (ppnt->p_type != PT_LOAD)
		return 0;

	/* Allow read-only executable segments to be loaded into L1 inst */
	if ((epnt->e_flags & EF_BFIN_CODE_IN_L1) &&
	    !(ppnt->p_flags & PF_W) && (ppnt->p_flags & PF_X))
		return 1;

	/* Allow writable non-executable segments to be loaded into L1 data */
	if ((epnt->e_flags & EF_BFIN_DATA_IN_L1) &&
	    (ppnt->p_flags & PF_W) && !(ppnt->p_flags & PF_X))
		return 1;

	/*
	 * These L1 memory addresses are also used in GNU ld and linux kernel.
	 * They need to be kept synchronized.
	 */
	switch (ppnt->p_vaddr) {
	case 0xff700000:
	case 0xff800000:
	case 0xff900000:
	case 0xffa00000:
	case 0xfeb00000:
	case 0xfec00000:
		return 1;
	default:
		return 0;
	}
}

static __always_inline char *
__dl_map_segment(Elf32_Ehdr *epnt, Elf32_Phdr *ppnt, int infile, int flags)
{
	void *addr;
	unsigned long sram_flags = 0;

	/* Handle L1 inst mappings */
	if (((epnt->e_flags & EF_BFIN_CODE_IN_L1) || ppnt->p_vaddr == 0xffa00000) &&
	    !(ppnt->p_flags & PF_W) && (ppnt->p_flags & PF_X))
	{
		size_t size = (ppnt->p_vaddr & ADDR_ALIGN) + ppnt->p_filesz;
		void *status = _dl_mmap(NULL, size, LXFLAGS(ppnt->p_flags),
			flags | MAP_EXECUTABLE | MAP_DENYWRITE,
			infile, ppnt->p_offset & OFFS_ALIGN);
		if (_dl_mmap_check_error(status))
			return NULL;

		addr = _dl_sram_alloc(ppnt->p_filesz, L1_INST_SRAM);
		if (addr)
			_dl_dma_memcpy(addr, status + (ppnt->p_vaddr & ADDR_ALIGN), ppnt->p_filesz);
		else
			_dl_dprintf(2, "%s:%s: sram allocation %#x failed\n",
				_dl_progname, __func__, ppnt->p_vaddr);

		_dl_munmap(status, size);
		return addr;
	}

	/* Handle L1 data mappings */
	if (((epnt->e_flags & EF_BFIN_DATA_IN_L1) ||
	     ppnt->p_vaddr == 0xff700000 ||
	     ppnt->p_vaddr == 0xff800000 ||
	     ppnt->p_vaddr == 0xff900000) &&
	    (ppnt->p_flags & PF_W) && !(ppnt->p_flags & PF_X))
	{
		switch (ppnt->p_vaddr) {
		case 0xff800000: sram_flags = L1_DATA_A_SRAM; break;
		case 0xff900000: sram_flags = L1_DATA_B_SRAM; break;
		default:         sram_flags = L1_DATA_SRAM;   break;
		}
	}

	/* Handle L2 mappings */
	if (ppnt->p_vaddr == 0xfeb00000 || ppnt->p_vaddr == 0xfec00000)
		sram_flags = L2_SRAM;

	if (sram_flags) {
		addr = _dl_sram_alloc(ppnt->p_memsz, sram_flags);
		if (addr) {
			if (_DL_PREAD(infile, addr, ppnt->p_filesz, ppnt->p_offset) != ppnt->p_filesz) {
				_dl_sram_free(addr);
				return NULL;
			}
			if (ppnt->p_filesz < ppnt->p_memsz)
				_dl_memset(addr + ppnt->p_filesz, 0, ppnt->p_memsz - ppnt->p_filesz);
		} else
			_dl_dprintf(2, "%s:%s: sram allocation %#x failed\n",
				_dl_progname, __func__, ppnt->p_vaddr);
		return addr;
	}

	return 0;
}