]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/open-amp.git/blob - porting/system/generic/machine/zynq7/machine.c
96908e1f6a0a45b55938a6844c8c1d46b2b95c74
[processor-sdk/open-amp.git] / porting / system / generic / machine / zynq7 / machine.c
1 /*
2  * Copyright (c) 2014, Mentor Graphics Corporation
3  * All rights reserved.
4  *
5  * Copyright (c) 2015 Xilinx, Inc. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  * 3. Neither the name of the <ORGANIZATION> nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <stdio.h>
32 #include <string.h>
33 #include "machine.h"
34 #include "openamp/env.h"
35 unsigned char ARM_AR_ISR_IRQ_Data[ARM_AR_ISR_STACK_SIZE];
36 unsigned char ARM_AR_ISR_FIQ_Data[ARM_AR_ISR_STACK_SIZE];
37 unsigned char ARM_AR_ISR_SUP_Stack[ARM_AR_ISR_STACK_SIZE];
38 unsigned char ARM_AR_ISR_SYS_Stack[ARM_AR_ISR_STACK_SIZE];
40 static inline unsigned int get_cpu_id_arm(void);
42 int zc702evk_gic_initialize()
43 {
45         unsigned long reg_val;
47         /* Disable architecture interrupts (IRQ and FIQ)
48          * before initialization */
49         ARM_AR_CPSR_CXSF_READ(&reg_val);
50         reg_val |= (0x02 << 6);
51         ARM_AR_CPSR_CXSF_WRITE(reg_val);
53         zc702evk_gic_pr_int_initialize();
55         /* Enable architecture Interrupts */
56         ARM_AR_CPSR_CXSF_READ(&reg_val);
57         reg_val &= ~(0x02 << 6);
58         ARM_AR_CPSR_CXSF_WRITE(reg_val);
60         return 0;
61 }
63 /* Only applicable for remote/slave node */
64 void zc702evk_gic_pr_int_initialize(void)
65 {
67         /* Disable the GIC controller */
68         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_CTRL, 0x00000000);
70         /* Enable the interrupt distributor controller */
71         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_CTRL, INT_DIST_ENABLE);
73         /* Secondary cores  just need to disable their private interrupts */
74         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_ENABLE_CLEAR + 0x00,
75                     0xffffffff);
76         /* 0  - 31 */
78         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_CONFIG + 0x00, 0xAAAAAAAA);
79         /* 0  - 15 */
80         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_CONFIG + 0x04, 0xAAAAAAAA);
82         /* Disable the CPU Interface */
83         MEM_WRITE32(INT_GIC_CPU_BASE + INT_GIC_CPU_CTRL, 0x00000000);
85         /* Allow interrupts with more priority (i.e. lower number) than FF */
86         MEM_WRITE32(INT_GIC_CPU_BASE + INT_GIC_CPU_PRIORITY, 0x000000FF);
88         /* No binary point */
89         MEM_WRITE32(INT_GIC_CPU_BASE + INT_GIC_CPU_POINT, 0x00000000);
91         /* Enable the CPU Interface */
92         MEM_WRITE32(INT_GIC_CPU_BASE + INT_GIC_CPU_CTRL, INT_CPU_ENABLE);
93 }
95 int platform_interrupt_enable(unsigned int vector_id, unsigned int polarity,
96                               unsigned int priority)
97 {
98         unsigned long reg_offset;
99         unsigned long bit_shift;
100         unsigned long temp32 = 0;
101         unsigned long targ_cpu;
103         temp32 = get_cpu_id_arm();
105         /* Determine the necessary bit shift in this target / priority register
106            for this interrupt vector ID */
107         bit_shift = ((vector_id) % 4) * 8;
109         /* Build a target value based on the bit shift calculated above and the CPU core
110            that this code is executing on */
111         targ_cpu = (1 << temp32) << bit_shift;
113         /* Determine the Global interrupt controller target / priority register
114            offset for this interrupt vector ID
115            NOTE:  Each target / priority register supports 4 interrupts */
116         reg_offset = ((vector_id) / 4) * 4;
118         /* Read-modify-write the priority register for this interrupt */
119         temp32 = MEM_READ32(INT_GIC_DIST_BASE + INT_GIC_DIST_PRI + reg_offset);
121         /* Set new priority. */
122         temp32 |= (priority << (bit_shift + 4));
123         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_PRI + reg_offset, temp32);
125         /* Read-modify-write the target register for this interrupt to allow this
126            cpu to accept this interrupt */
127         temp32 =
128             MEM_READ32(INT_GIC_DIST_BASE + INT_GIC_DIST_TARGET + reg_offset);
129         temp32 |= targ_cpu;
130         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_TARGET + reg_offset,
131                     temp32);
133         /* Determine the Global interrupt controller enable set register offset
134            for this vector ID
135            NOTE:  There are 32 interrupts in each enable set register */
136         reg_offset = (vector_id / 32) * 4;
138         /* Write to the appropriate bit in the enable set register for this
139            vector ID to enable the interrupt */
141         temp32 = (1UL << (vector_id - (reg_offset * 0x08)));
142         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_ENABLE_SET + reg_offset,
143                     temp32);
145         /* Return the vector ID */
146         return (vector_id);
149 int platform_interrupt_disable(unsigned int vector_id)
151         unsigned long reg_offset;
152         unsigned long bit_shift;
153         unsigned long temp32 = 0;
154         unsigned long targ_cpu;
156         temp32 = get_cpu_id_arm();
158         /* Determine the Global interrupt controller enable set register offset
159            for this vector ID
160            NOTE:  There are 32 interrupts in each enable set register */
161         reg_offset = (vector_id / 32) * 4;
163         /* Write to the appropriate bit in the enable clear register for this
164            vector ID to disable the interrupt */
166         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_ENABLE_CLEAR + reg_offset,
167                     (1UL << (vector_id - (reg_offset * 0x08))));
169         /* Determine the Global interrupt controller target register offset for
170            this interrupt vector ID
171            NOTE:  Each target register supports 4 interrupts */
172         reg_offset = (vector_id / 4) * 4;
174         /* Determine the necessary bit shift in this target register for this
175            vector ID */
176         bit_shift = (vector_id % 4) * 8;
178         /* Build a value based on the bit shift calculated above and the CPU core
179            that this code is executing on */
180         targ_cpu = (1 << temp32) << bit_shift;
182         /* Read-modify-write the target register for this interrupt and remove this cpu from
183            accepting this interrupt */
184         temp32 =
185             MEM_READ32(INT_GIC_DIST_BASE + INT_GIC_DIST_TARGET + reg_offset);
186         temp32 &= ~targ_cpu;
188         MEM_WRITE32(INT_GIC_DIST_BASE + INT_GIC_DIST_TARGET + reg_offset,
189                     temp32);
191         /* Return the vector ID */
192         return (vector_id);
195 void arm_arch_install_isr_vector_table(unsigned long addr)
197         unsigned long arch = 0;
198         void *dst_addr;
200         /* Assign destination address of vector table to RAM address */
201         dst_addr = (void *)addr;
202         /* Read Main ID Register (MIRD) */
203         ARM_AR_CP_READ(p15, 0, &arch, c0, c0, 0);
205         /* Check if Cortex-A series of ARMv7 architecture. */
206         if (((arch & MIDR_ARCH_MASK) >> 16) == MIDR_ARCH_ARMV7
207             && ((arch & MIDR_PART_NO_MASK) >> 4)
208             == MIDR_PART_NO_CORTEX_A) {
209                 /* Set vector base address */
210                 ARM_AR_CP_WRITE(p15, 0, dst_addr, c12, c0, 0);
211                 ARM_AR_NOP_EXECUTE();
212                 ARM_AR_NOP_EXECUTE();
213                 ARM_AR_NOP_EXECUTE();
214         }
217 extern void bm_env_isr(int vector);
219 /* IRQ handler */
220 void __attribute__ ((interrupt("IRQ"))) __cs3_isr_irq()
222         unsigned long raw_irq;
223         int irq_vector;
225         /* Read the Interrupt ACK register */
226         raw_irq = MEM_READ32(INT_GIC_CPU_BASE + INT_GIC_CPU_ACK);
228         /* mask interrupt to get vector */
229         irq_vector = raw_irq & INT_ACK_MASK;
231         bm_env_isr(irq_vector);
233         /* Clear the interrupt */
234         MEM_WRITE32(INT_GIC_CPU_BASE + INT_GIC_CPU_ENDINT, raw_irq);
237 /* FIQ Handler */
238 void __attribute__ ((interrupt("FIQ"))) __cs3_isr_fiq()
240         while (1) ;
243 static inline unsigned int get_cpu_id_arm(void)
245         unsigned long cpu_id = 0;
247         asm volatile ("MRC p15 ,"
248                       "0," "%0," "c0," "c0," "5":[cpu_id] "=&r"(cpu_id)
249                       : /* No inputs */ );
251         /*
252          * Return cpu id to caller, extract last two bits from Multiprocessor
253          * Affinity Register */
254         return (cpu_id & 0x03);
257 int old_value = 0;
259 void restore_global_interrupts()
261         ARM_AR_INT_BITS_SET(old_value);
264 void disable_global_interrupts()
266         int value = 0;
267         ARM_AR_INT_BITS_GET(&value);
268         if (value != old_value) {
269                 ARM_AR_INT_BITS_SET(ARM_AR_INTERRUPTS_DISABLE_BITS);
270                 old_value = value;
271         }
274 void init_arm_stacks(void)
277         /* Switch to IRQ mode (keeping interrupts disabled) */
278         ARM_AR_CPSR_C_WRITE(ARM_AR_INT_CPSR_IRQ_MODE |
279                             ARM_AR_INTERRUPTS_DISABLE_BITS);
281         /* Set IRQ stack pointer */
282         ARM_AR_SP_WRITE(ARM_GE_STK_ALIGN
283                         (&ARM_AR_ISR_IRQ_Data[ARM_AR_ISR_STACK_SIZE - 1]));
285         /* Switch to FIQ mode (keeping interrupts disabled) */
286         ARM_AR_CPSR_C_WRITE(ARM_AR_INT_CPSR_FIQ_MODE |
287                             ARM_AR_INTERRUPTS_DISABLE_BITS);
289         /* Set FIQ stack pointer */
290         ARM_AR_SP_WRITE(ARM_GE_STK_ALIGN
291                         (ARM_AR_ISR_FIQ_Data[ARM_AR_ISR_STACK_SIZE - 1]));
293         /* Switch to Supervisor mode (keeping interrupts disabled) */
294         ARM_AR_CPSR_C_WRITE(ARM_AR_INT_CPSR_SUP_MODE |
295                             ARM_AR_INTERRUPTS_DISABLE_BITS);
297         /* Set Supervisor stack pointer */
298         ARM_AR_SP_WRITE(ARM_GE_STK_ALIGN
299                         (&ARM_AR_ISR_SUP_Stack[ARM_AR_ISR_STACK_SIZE - 1]));
301         /* Switch to System mode (keeping interrupts disabled) */
302         ARM_AR_CPSR_C_WRITE(ARM_AR_INT_CPSR_SYS_DISABLED);
305 /***********************************************************************
306  *
307  *  arm_ar_mem_enable_mmu
308  *
309  *  Enables MMU and MAP the required memory regions.
310  *
311  ***********************************************************************/
312 int arm_ar_mem_enable_mmu()
314         unsigned int cp15_ctrl_val;
315         void *tlb_mem = (void *)TLB_MEM_START;
317         ARM_AR_MEM_CACHE_ALL_INVALIDATE();
319         /* Read current CP15 control register value */
320         ARM_AR_CP_READ(ARM_AR_CP15, 0, &cp15_ctrl_val, ARM_AR_C1, ARM_AR_C0, 0);
322         /* Clear the V bit(13) to set Normal exception vectors range. */
323         cp15_ctrl_val &= ~(ARM_AR_MEM_CP15_CTRL_V);
325         /* Clear the alignment bit(1) to enable unaligned memory accesses */
326         cp15_ctrl_val &= ~(ARM_AR_MEM_CP15_CTRL_A);
328         /* Write updated CP15 control register value */
329         ARM_AR_CP_WRITE(ARM_AR_CP15, 0, cp15_ctrl_val, ARM_AR_C1, ARM_AR_C0, 0);
331         ARM_AR_NOP_EXECUTE();
332         ARM_AR_NOP_EXECUTE();
333         ARM_AR_NOP_EXECUTE();
335         /* Check alignment of available memory pointer */
336         if (!(MEM_ALIGNED_CHECK(tlb_mem, ARM_AR_MEM_TTB_SIZE))) {
337                 /* Align the pointer to the required boundary */
338                 tlb_mem = MEM_PTR_ALIGN(tlb_mem, ARM_AR_MEM_TTB_SIZE);
339         }
341         /* Clear the entire translation table */
342         memset(tlb_mem, 0x00, ARM_AR_MEM_TTB_SIZE);
344         /* Set translation table base address */
345         ARM_AR_CP_WRITE(ARM_AR_CP15, 0, tlb_mem, ARM_AR_C2, ARM_AR_C0, 0);
347         ARM_AR_CP_READ(ARM_AR_CP15, 0, &cp15_ctrl_val, ARM_AR_C2, ARM_AR_C0, 0);
349         /* Map the given memory regions here */
350         arm_ar_map_mem_region(ELF_START, ELF_START, ELF_END, 0, WRITEBACK);
351         arm_ar_map_mem_region((unsigned int)tlb_mem, (unsigned int)tlb_mem,
352                               TLB_SIZE, 0, NOCACHE);
353         arm_ar_map_mem_region(PERIPH_BASE, PERIPH_BASE,
354                               PERIPH_SIZE, 1, NOCACHE);
355         arm_ar_map_mem_region(SLCR_BASE, SLCR_BASE, SLCR_SIZE, 1, NOCACHE);
356         arm_ar_map_mem_region(CPU_BASE, CPU_BASE, CPU_SIZE, 1, NOCACHE);
358         /* Set the domain access for domain D0 */
359         ARM_AR_CP_WRITE(ARM_AR_CP15, 0, ARM_AR_MEM_DOMAIN_D0_MANAGER_ACCESS,
360                         ARM_AR_C3, ARM_AR_C0, 0);
362         ARM_AR_CP_READ(ARM_AR_CP15, 0, &cp15_ctrl_val, ARM_AR_C3, ARM_AR_C0, 0);
364         /* Invalidate all TLB entries before enabling the MMU */
365         ARM_AR_CP_WRITE(ARM_AR_CP15, 0, 0, ARM_AR_C8, ARM_AR_C7, 0);
367         /* Read current CP15 control register value */
368         ARM_AR_CP_READ(ARM_AR_CP15, 0, &cp15_ctrl_val, ARM_AR_C1, ARM_AR_C0, 0);
370         /* Set instruction cache enable / data cache enable / MMU enable bits */
371         cp15_ctrl_val |= (ARM_AR_MEM_CP15_CTRL_I | ARM_AR_MEM_CP15_CTRL_C
372                           | ARM_AR_MEM_CP15_CTRL_M | ARM_AR_MEM_CP15_CTRL_Z);
374         /* Write updated CP15 control register value */
375         ARM_AR_CP_WRITE(ARM_AR_CP15, 0, cp15_ctrl_val, ARM_AR_C1, ARM_AR_C0, 0);
377         ARM_AR_NOP_EXECUTE();
378         ARM_AR_NOP_EXECUTE();
379         ARM_AR_NOP_EXECUTE();
381         return 0;
384 /***********************************************************************
385  *
386  *
387  * arm_ar_map_mem_region
388  *
389  *
390  * This function sets-up the region of memory based on the given
391  * attributes
393  *
394  * @param vrt_addr       - virtual address of region
395  * @param phy_addr       - physical address of region
396  * @parma size           - size of region
397  * @param is_mem_mapped  - memory mapped or not
399  * @param cache_type     - cache type of region
400  *
401  *
402  *   OUTPUTS
403  *
404  *       None
405  *
406  ***********************************************************************/
407 void arm_ar_map_mem_region(unsigned int vrt_addr, unsigned int phy_addr,
408                            unsigned int size, int is_mem_mapped,
409                            CACHE_TYPE cache_type)
411         unsigned int section_offset;
412         unsigned int ttb_offset;
413         unsigned int ttb_value;
414         unsigned int ttb_base;
416         /* Read ttb base address */
417         ARM_AR_CP_READ(ARM_AR_CP15, 0, &ttb_base, ARM_AR_C2, ARM_AR_C0, 0);
419         /* Ensure the virtual and physical addresses are aligned on a
420            section boundary */
421         vrt_addr &= ARM_AR_MEM_TTB_SECT_SIZE_MASK;
422         phy_addr &= ARM_AR_MEM_TTB_SECT_SIZE_MASK;
424         /* Loop through entire region of memory (one MMU section at a time).
425            Each section requires a TTB entry. */
426         for (section_offset = 0; section_offset < size; section_offset +=
427              ARM_AR_MEM_TTB_SECT_SIZE) {
429                 /* Calculate translation table entry offset for this memory section */
430                 ttb_offset = ((vrt_addr + section_offset)
431                               >> ARM_AR_MEM_TTB_SECT_TO_DESC_SHIFT);
433                 /* Build translation table entry value */
434                 ttb_value = (phy_addr + section_offset)
435                     | ARM_AR_MEM_TTB_DESC_ALL_ACCESS;
437                 if (!is_mem_mapped) {
439                         /* Set cache related bits in translation table entry.
440                            NOTE: Default is uncached instruction and data. */
441                         if (cache_type == WRITEBACK) {
442                                 /* Update translation table entry value */
443                                 ttb_value |=
444                                     (ARM_AR_MEM_TTB_DESC_B |
445                                      ARM_AR_MEM_TTB_DESC_C);
446                         } else if (cache_type == WRITETHROUGH) {
447                                 /* Update translation table entry value */
448                                 ttb_value |= ARM_AR_MEM_TTB_DESC_C;
449                         }
450                         /* In case of un-cached memory, set TEX 0 bit to set memory
451                            attribute to normal. */
452                         else if (cache_type == NOCACHE) {
453                                 ttb_value |= ARM_AR_MEM_TTB_DESC_TEX;
454                         }
455                 }
457                 /* Write translation table entry value to entry address */
458                 MEM_WRITE32(ttb_base + ttb_offset, ttb_value);
460         }                       /* for loop */
463 void platform_map_mem_region(unsigned int vrt_addr, unsigned int phy_addr,
464                              unsigned int size, unsigned int flags)
466         int is_mem_mapped = 0;
467         int cache_type = 0;
469         if ((flags & (0x0f << 4)) == MEM_MAPPED) {
470                 is_mem_mapped = 1;
471         }
473         if ((flags & 0x0f) == WB_CACHE) {
474                 cache_type = WRITEBACK;
475         } else if ((flags & 0x0f) == WT_CACHE) {
476                 cache_type = WRITETHROUGH;
477         } else {
478                 cache_type = NOCACHE;
479         }
481         arm_ar_map_mem_region(vrt_addr, phy_addr, size, is_mem_mapped,
482                               cache_type);
485 void platform_cache_all_flush_invalidate()
487         ARM_AR_MEM_DCACHE_ALL_OP(1);
490 void platform_cache_disable()
492         ARM_AR_MEM_CACHE_DISABLE();
495 unsigned long platform_vatopa(void *addr)
497         return (((unsigned long)addr & (~(0x0fff << 20))) | (0x08 << 24));
500 void *platform_patova(unsigned long addr)
502         return ((void *)addr);
506 /*==================================================================*/
507 /* The function definitions below are provided to prevent the build */
508 /* warnings for missing I/O function stubs in case of unhosted libs */
509 /*==================================================================*/
511 #include            <sys/stat.h>
513 /**
514  * _fstat
515  *
516  * Status of an open file. For consistency with other minimal
517  * implementations in these examples, all files are regarded
518  * as character special devices.
519  *
520  * @param file    - Unused.
521  * @param st      - Status structure.
522  *
523  *
524  *       A constant value of 0.
525  *
526  **/
527 __attribute__ ((weak))
528 int _fstat(int file, struct stat *st)
530         return (0);
533 /**
534  *  isatty
535  *
536  *
537  * Query whether output stream is a terminal. For consistency
538  * with the other minimal implementations, which only support
539  * output to stdout, this minimal implementation is suggested
540  *
541  * @param file    - Unused
542  *
543  * @return s - A constant value of 1.
544  *
545  */
546 __attribute__ ((weak))
547 int _isatty(int file)
549         return (1);
552 /**
553  *_lseek
554  *
555  * Set position in a file. Minimal implementation.
557  *
558  * @param file    - Unused
559  *
560  * @param ptr     - Unused
561  *
562  * @param dir     - Unused
563  *
564  * @return - A constant value of 0.
565  *
566  */
567 __attribute__ ((weak))
568 int _lseek(int file, int ptr, int dir)
570         return (0);
573 /**
574  *  _open
575  *
576  * Open a file.  Minimal implementation
577  *
578  * @param filename    - Unused
579  * @param flags       - Unused
580  * @param mode        - Unused
581  *
582  * return -  A constant value of 1.
583  *
584  */
585 __attribute__ ((weak))
586 int _open(const char *filename, int flags, int mode)
588         /* Any number will work. */
589         return (1);
592 /**
593  *  _close
594  *
595  * Close a file.  Minimal implementation.
596  *
597  *
598  * @param file    - Unused
599  *
600  *
601  * return A constant value of -1.
602  *
603  */
604 __attribute__ ((weak))
605 int _close(int file)
607         return (-1);
610 /**
611  * _read
612  *
613  *  Low level function to redirect IO to serial.
614  *
615  * @param fd          - Unused
616  * @param buffer      - Buffer where read data will be placed.
617  * @param buflen      - Size (in bytes) of buffer.
618  *
619  * return -  A constant value of 1.
620  *
621  */
622 __attribute__ ((weak))
623 int _read(int fd, char *buffer, int buflen)
625         return -1;
628 /**
629  * _write
630  *
631  * Low level function to redirect IO to serial.
632  *
633  *
634  * @param file                          - Unused
635  * @param CHAR *ptr                         - String to output
636  * @param len                           - Length of the string
637  *
638  * return len                            - The length of the string
639  *
640  */
641 __attribute__ ((weak))
642 int _write(int file, const char *ptr, int len)
644         return 0;