aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/mm/elf.c
blob: 55e58e93bfc58025d4de4815794c2affd9638f9a (plain) (blame)
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * Copyright 2010 Tilera Corporation. All Rights Reserved.
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation, version 2.
 *
 *   This program is distributed in the hope that it will be useful, but
 *   WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 *   NON INFRINGEMENT.  See the GNU General Public License for
 *   more details.
 */

#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/binfmts.h>
#include <linux/compat.h>
#include <linux/mman.h>
#include <linux/elf.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>

/* Notify a running simulator, if any, that an exec just occurred. */
static void sim_notify_exec(const char *binary_name)
{
	unsigned char c;
	do {
		c = *binary_name++;
		__insn_mtspr(SPR_SIM_CONTROL,
			     (SIM_CONTROL_OS_EXEC
			      | (c << _SIM_CONTROL_OPERATOR_BITS)));

	} while (c);
}

static int notify_exec(void)
{
	int retval = 0;  /* failure */
	struct vm_area_struct *vma = current->mm->mmap;
	while (vma) {
		if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file)
			break;
		vma = vma->vm_next;
	}
	if (vma) {
		char *buf = (char *) __get_free_page(GFP_KERNEL);
		if (buf) {
			char *path = d_path(&vma->vm_file->f_path,
					    buf, PAGE_SIZE);
			if (!IS_ERR(path)) {
				sim_notify_exec(path);
				retval = 1;
			}
			free_page((unsigned long)buf);
		}
	}
	return retval;
}

/* Notify a running simulator, if any, that we loaded an interpreter. */
static void sim_notify_interp(unsigned long load_addr)
{
	size_t i;
	for (i = 0; i < sizeof(load_addr); i++) {
		unsigned char c = load_addr >> (i * 8);
		__insn_mtspr(SPR_SIM_CONTROL,
			     (SIM_CONTROL_OS_INTERP
			      | (c << _SIM_CONTROL_OPERATOR_BITS)));
	}
}


/* Kernel address of page used to map read-only kernel data into userspace. */
static void *vdso_page;

/* One-entry array used for install_special_mapping. */
static struct page *vdso_pages[1];

static int __init vdso_setup(void)
{
	vdso_page = (void *)get_zeroed_page(GFP_ATOMIC);
	memcpy(vdso_page, __rt_sigreturn, __rt_sigreturn_end - __rt_sigreturn);
	vdso_pages[0] = virt_to_page(vdso_page);
	return 0;
}
device_initcall(vdso_setup);

const char *arch_vma_name(struct vm_area_struct *vma)
{
	if (vma->vm_private_data == vdso_pages)
		return "[vdso]";
#ifndef __tilegx__
	if (vma->vm_start == MEM_USER_INTRPT)
		return "[intrpt]";
#endif
	return NULL;
}

int arch_setup_additional_pages(struct linux_binprm *bprm,
				int executable_stack)
{
	struct mm_struct *mm = current->mm;
	unsigned long vdso_base;
	int retval = 0;

	/*
	 * Notify the simulator that an exec just occurred.
	 * If we can't find the filename of the mapping, just use
	 * whatever was passed as the linux_binprm filename.
	 */
	if (!notify_exec())
		sim_notify_exec(bprm->filename);

	down_write(&mm->mmap_sem);

	/*
	 * MAYWRITE to allow gdb to COW and set breakpoints
	 *
	 * Make sure the vDSO gets into every core dump.  Dumping its
	 * contents makes post-mortem fully interpretable later
	 * without matching up the same kernel and hardware config to
	 * see what PC values meant.
	 */
	vdso_base = VDSO_BASE;
	retval = install_special_mapping(mm, vdso_base, PAGE_SIZE,
					 VM_READ|VM_EXEC|
					 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC|
					 VM_ALWAYSDUMP,
					 vdso_pages);

#ifndef __tilegx__
	/*
	 * Set up a user-interrupt mapping here; the user can't
	 * create one themselves since it is above TASK_SIZE.
	 * We make it unwritable by default, so the model for adding
	 * interrupt vectors always involves an mprotect.
	 */
	if (!retval) {
		unsigned long addr = MEM_USER_INTRPT;
		addr = mmap_region(NULL, addr, INTRPT_SIZE,
				   MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE,
				   VM_READ|VM_EXEC|
				   VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 0);
		if (addr > (unsigned long) -PAGE_SIZE)
			retval = (int) addr;
	}
#endif

	up_write(&mm->mmap_sem);

	return retval;
}


void elf_plat_init(struct pt_regs *regs, unsigned long load_addr)
{
	/* Zero all registers. */
	memset(regs, 0, sizeof(*regs));

	/* Report the interpreter's load address. */
	sim_notify_interp(load_addr);
}
7' href='#n717'>717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895






























































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                               
.psize 0
/*
  wanXL serial card driver for Linux
  card firmware part

  Copyright (C) 2003 Krzysztof Halasa <khc@pm.waw.pl>

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License
  as published by the Free Software Foundation.




	DPRAM BDs:
	0x000 - 0x050 TX#0	0x050 - 0x140 RX#0
	0x140 - 0x190 TX#1	0x190 - 0x280 RX#1
	0x280 - 0x2D0 TX#2	0x2D0 - 0x3C0 RX#2
	0x3C0 - 0x410 TX#3	0x410 - 0x500 RX#3


	000 5FF 1536 Bytes Dual-Port RAM User Data / BDs
	600 6FF 256 Bytes Dual-Port RAM User Data / BDs
	700 7FF 256 Bytes Dual-Port RAM User Data / BDs
	C00 CBF 192 Bytes Dual-Port RAM Parameter RAM Page 1
	D00 DBF 192 Bytes Dual-Port RAM Parameter RAM Page 2
	E00 EBF 192 Bytes Dual-Port RAM Parameter RAM Page 3
	F00 FBF 192 Bytes Dual-Port RAM Parameter RAM Page 4

	local interrupts		    level
	NMI					7
	PIT timer, CPM (RX/TX complete)		4
	PCI9060	DMA and PCI doorbells		3
	Cable - not used			1
*/

#include <linux/hdlc.h>
#include "wanxl.h"

/* memory addresses and offsets */

MAX_RAM_SIZE	= 16 * 1024 * 1024	// max RAM supported by hardware

PCI9060_VECTOR	= 0x0000006C
CPM_IRQ_BASE	= 0x40
ERROR_VECTOR	= CPM_IRQ_BASE * 4
SCC1_VECTOR	= (CPM_IRQ_BASE + 0x1E) * 4
SCC2_VECTOR	= (CPM_IRQ_BASE + 0x1D) * 4
SCC3_VECTOR	= (CPM_IRQ_BASE + 0x1C) * 4
SCC4_VECTOR	= (CPM_IRQ_BASE + 0x1B) * 4
CPM_IRQ_LEVEL	= 4
TIMER_IRQ	= 128
TIMER_IRQ_LEVEL = 4
PITR_CONST	= 0x100 + 16		// 1 Hz timer

MBAR		= 0x0003FF00

VALUE_WINDOW	= 0x40000000
ORDER_WINDOW	= 0xC0000000

PLX		= 0xFFF90000

CSRA		= 0xFFFB0000
CSRB		= 0xFFFB0002
CSRC		= 0xFFFB0004
CSRD		= 0xFFFB0006
STATUS_CABLE_LL		= 0x2000
STATUS_CABLE_DTR	= 0x1000

DPRBASE		= 0xFFFC0000

SCC1_BASE	= DPRBASE + 0xC00
MISC_BASE	= DPRBASE + 0xCB0
SCC2_BASE	= DPRBASE + 0xD00
SCC3_BASE	= DPRBASE + 0xE00
SCC4_BASE	= DPRBASE + 0xF00

// offset from SCCx_BASE
// SCC_xBASE contain offsets from DPRBASE and must be divisible by 8
SCC_RBASE	= 0		// 16-bit RxBD base address
SCC_TBASE	= 2		// 16-bit TxBD base address
SCC_RFCR	= 4		// 8-bit Rx function code
SCC_TFCR	= 5		// 8-bit Tx function code
SCC_MRBLR	= 6		// 16-bit maximum Rx buffer length
SCC_C_MASK	= 0x34		// 32-bit CRC constant
SCC_C_PRES	= 0x38		// 32-bit CRC preset
SCC_MFLR	= 0x46		// 16-bit max Rx frame length (without flags)

REGBASE		= DPRBASE + 0x1000
PICR		= REGBASE + 0x026	// 16-bit periodic irq control
PITR		= REGBASE + 0x02A	// 16-bit periodic irq timing
OR1		= REGBASE + 0x064	// 32-bit RAM bank #1 options
CICR		= REGBASE + 0x540	// 32(24)-bit CP interrupt config
CIMR		= REGBASE + 0x548	// 32-bit CP interrupt mask
CISR		= REGBASE + 0x54C	// 32-bit CP interrupts in-service
PADIR		= REGBASE + 0x550	// 16-bit PortA data direction bitmap
PAPAR		= REGBASE + 0x552	// 16-bit PortA pin assignment bitmap
PAODR		= REGBASE + 0x554	// 16-bit PortA open drain bitmap
PADAT		= REGBASE + 0x556	// 16-bit PortA data register

PCDIR		= REGBASE + 0x560	// 16-bit PortC data direction bitmap
PCPAR		= REGBASE + 0x562	// 16-bit PortC pin assignment bitmap
PCSO		= REGBASE + 0x564	// 16-bit PortC special options
PCDAT		= REGBASE + 0x566	// 16-bit PortC data register
PCINT		= REGBASE + 0x568	// 16-bit PortC interrupt control
CR		= REGBASE + 0x5C0	// 16-bit Command register

SCC1_REGS	= REGBASE + 0x600
SCC2_REGS	= REGBASE + 0x620
SCC3_REGS	= REGBASE + 0x640
SCC4_REGS	= REGBASE + 0x660
SICR		= REGBASE + 0x6EC	// 32-bit SI clock route

// offset from SCCx_REGS
SCC_GSMR_L	= 0x00	// 32 bits
SCC_GSMR_H	= 0x04	// 32 bits
SCC_PSMR	= 0x08	// 16 bits
SCC_TODR	= 0x0C	// 16 bits
SCC_DSR		= 0x0E	// 16 bits
SCC_SCCE	= 0x10	// 16 bits
SCC_SCCM	= 0x14	// 16 bits
SCC_SCCS	= 0x17	// 8 bits

#if QUICC_MEMCPY_USES_PLX
	.macro memcpy_from_pci src, dest, len // len must be < 8 MB
	addl #3, \len
	andl #0xFFFFFFFC, \len		// always copy n * 4 bytes
	movel \src, PLX_DMA_0_PCI
	movel \dest, PLX_DMA_0_LOCAL
	movel \len, PLX_DMA_0_LENGTH
	movel #0x0103, PLX_DMA_CMD_STS	// start channel 0 transfer
	bsr memcpy_from_pci_run
	.endm

	.macro memcpy_to_pci src, dest, len
	addl #3, \len
	andl #0xFFFFFFFC, \len		// always copy n * 4 bytes
	movel \src, PLX_DMA_1_LOCAL
	movel \dest, PLX_DMA_1_PCI
	movel \len, PLX_DMA_1_LENGTH
	movel #0x0301, PLX_DMA_CMD_STS	// start channel 1 transfer
	bsr memcpy_to_pci_run
	.endm

#else

	.macro memcpy src, dest, len	// len must be < 65536 bytes
	movel %d7, -(%sp)		// src and dest must be < 256 MB
	movel \len, %d7			// bits 0 and 1
	lsrl #2, \len
	andl \len, \len
	beq 99f				// only 0 - 3 bytes
	subl #1, \len			// for dbf
98:	movel (\src)+, (\dest)+
	dbfw \len, 98b
99:	movel %d7, \len
	btstl #1, \len
	beq 99f
	movew (\src)+, (\dest)+
99:	btstl #0, \len
	beq 99f
	moveb (\src)+, (\dest)+
99:
	movel (%sp)+, %d7
	.endm

	.macro memcpy_from_pci src, dest, len
	addl #VALUE_WINDOW, \src
	memcpy \src, \dest, \len
	.endm

	.macro memcpy_to_pci src, dest, len
	addl #VALUE_WINDOW, \dest
	memcpy \src, \dest, \len
	.endm
#endif


	.macro wait_for_command
99:	btstl #0, CR
	bne 99b
	.endm




/****************************** card initialization *******************/
	.text
	.global _start
_start:	bra init

	.org _start + 4
ch_status_addr:	.long 0, 0, 0, 0
rx_descs_addr:	.long 0

init:
#if DETECT_RAM
	movel OR1, %d0
	andl #0xF00007FF, %d0		// mask AMxx bits
	orl #0xFFFF800 & ~(MAX_RAM_SIZE - 1), %d0 // update RAM bank size
	movel %d0, OR1
#endif

	addl #VALUE_WINDOW, rx_descs_addr // PCI addresses of shared data
	clrl %d0			// D0 = 4 * port
init_1:	tstl ch_status_addr(%d0)
	beq init_2
	addl #VALUE_WINDOW, ch_status_addr(%d0)
init_2:	addl #4, %d0
	cmpl #4 * 4, %d0
	bne init_1

	movel #pci9060_interrupt, PCI9060_VECTOR
	movel #error_interrupt, ERROR_VECTOR
	movel #port_interrupt_1, SCC1_VECTOR
	movel #port_interrupt_2, SCC2_VECTOR
	movel #port_interrupt_3, SCC3_VECTOR
	movel #port_interrupt_4, SCC4_VECTOR
	movel #timer_interrupt, TIMER_IRQ * 4

	movel #0x78000000, CIMR		// only SCCx IRQs from CPM
	movew #(TIMER_IRQ_LEVEL << 8) + TIMER_IRQ, PICR	// interrupt from PIT
	movew #PITR_CONST, PITR

	// SCC1=SCCa SCC2=SCCb SCC3=SCCc SCC4=SCCd prio=4 HP=-1 IRQ=64-79
	movel #0xD41F40 + (CPM_IRQ_LEVEL << 13), CICR
	movel #0x543, PLX_DMA_0_MODE	// 32-bit, Ready, Burst, IRQ
	movel #0x543, PLX_DMA_1_MODE
	movel #0x0, PLX_DMA_0_DESC	// from PCI to local
	movel #0x8, PLX_DMA_1_DESC	// from local to PCI
	movel #0x101, PLX_DMA_CMD_STS	// enable both DMA channels
	// enable local IRQ, DMA, doorbells and PCI IRQ
	orl #0x000F0300, PLX_INTERRUPT_CS

#if DETECT_RAM
	bsr ram_test
#else
	movel #1, PLX_MAILBOX_5		// non-zero value = init complete
#endif
	bsr check_csr

	movew #0xFFFF, PAPAR		// all pins are clocks/data
	clrw PADIR			// first function
	clrw PCSO			// CD and CTS always active


/****************************** main loop *****************************/

main:	movel channel_stats, %d7	// D7 = doorbell + irq status
	clrl channel_stats

	tstl %d7
	bne main_1
	// nothing to do - wait for next event
	stop #0x2200			// supervisor + IRQ level 2
	movew #0x2700, %sr		// disable IRQs again
	bra main

main_1:	clrl %d0			// D0 = 4 * port
	clrl %d6			// D6 = doorbell to host value

main_l: btstl #DOORBELL_TO_CARD_CLOSE_0, %d7
	beq main_op
	bclrl #DOORBELL_TO_CARD_OPEN_0, %d7 // in case both bits are set
	bsr close_port
main_op:
	btstl #DOORBELL_TO_CARD_OPEN_0, %d7
	beq main_cl
	bsr open_port
main_cl:
	btstl #DOORBELL_TO_CARD_TX_0, %d7
	beq main_txend
	bsr tx
main_txend:
	btstl #TASK_SCC_0, %d7
	beq main_next
	bsr tx_end
	bsr rx

main_next:
	lsrl #1, %d7			// port status for next port
	addl #4, %d0			// D0 = 4 * next port
	cmpl #4 * 4, %d0
	bne main_l
	movel %d6, PLX_DOORBELL_FROM_CARD // signal the host
	bra main


/****************************** open port *****************************/

open_port:				// D0 = 4 * port, D6 = doorbell to host
	movel ch_status_addr(%d0), %a0	// A0 = port status address
	tstl STATUS_OPEN(%a0)
	bne open_port_ret		// port already open
	movel #1, STATUS_OPEN(%a0)	// confirm the port is open
// setup BDs
	clrl tx_in(%d0)
	clrl tx_out(%d0)
	clrl tx_count(%d0)
	clrl rx_in(%d0)

	movel SICR, %d1			// D1 = clock settings in SICR
	andl clocking_mask(%d0), %d1
	cmpl #CLOCK_TXFROMRX, STATUS_CLOCKING(%a0)
	bne open_port_clock_ext
	orl clocking_txfromrx(%d0), %d1
	bra open_port_set_clock

open_port_clock_ext:
	orl clocking_ext(%d0), %d1
open_port_set_clock:
	movel %d1, SICR			// update clock settings in SICR

	orw #STATUS_CABLE_DTR, csr_output(%d0)	// DTR on
	bsr check_csr			// call with disabled timer interrupt

// Setup TX descriptors
	movel first_buffer(%d0), %d1	// D1 = starting buffer address
	movel tx_first_bd(%d0), %a1	// A1 = starting TX BD address
	movel #TX_BUFFERS - 2, %d2	// D2 = TX_BUFFERS - 1 counter
	movel #0x18000000, %d3		// D3 = initial TX BD flags: Int + Last
	cmpl #PARITY_NONE, STATUS_PARITY(%a0)
	beq open_port_tx_loop
	bsetl #26, %d3			// TX BD flag: Transmit CRC
open_port_tx_loop:
	movel %d3, (%a1)+		// TX flags + length
	movel %d1, (%a1)+		// buffer address
	addl #BUFFER_LENGTH, %d1
	dbfw %d2, open_port_tx_loop

	bsetl #29, %d3			// TX BD flag: Wrap (last BD)
	movel %d3, (%a1)+		// Final TX flags + length
	movel %d1, (%a1)+		// buffer address

// Setup RX descriptors			// A1 = starting RX BD address
	movel #RX_BUFFERS - 2, %d2	// D2 = RX_BUFFERS - 1 counter
open_port_rx_loop:
	movel #0x90000000, (%a1)+	// RX flags + length
	movel %d1, (%a1)+		// buffer address
	addl #BUFFER_LENGTH, %d1
	dbfw %d2, open_port_rx_loop

	movel #0xB0000000, (%a1)+	// Final RX flags + length
	movel %d1, (%a1)+		// buffer address

// Setup port parameters
	movel scc_base_addr(%d0), %a1	// A1 = SCC_BASE address
	movel scc_reg_addr(%d0), %a2	// A2 = SCC_REGS address

	movel #0xFFFF, SCC_SCCE(%a2)	// clear status bits
	movel #0x0000, SCC_SCCM(%a2)	// interrupt mask

	movel tx_first_bd(%d0), %d1
	movew %d1, SCC_TBASE(%a1)	// D1 = offset of first TxBD
	addl #TX_BUFFERS * 8, %d1
	movew %d1, SCC_RBASE(%a1)	// D1 = offset of first RxBD
	moveb #0x8, SCC_RFCR(%a1)	// Intel mode, 1000
	moveb #0x8, SCC_TFCR(%a1)

// Parity settings
	cmpl #PARITY_CRC16_PR1_CCITT, STATUS_PARITY(%a0)
	bne open_port_parity_1