├── Makefile ├── README.md ├── arm_defs.h ├── boot.lds ├── cfb_console.c ├── ctype.c ├── ctype.h ├── defs.h ├── fb.c ├── fdt.c ├── fdt.h ├── fdt_ro.c ├── fdt_strerror.c ├── lib.c ├── lib.h ├── libfdt.h ├── libfdt_env.h ├── libfdt_internal.h ├── lmb.c ├── lmb.h ├── main.c ├── start.S ├── string.c ├── string.h ├── tegra.h ├── usb_descriptors.h ├── usbd.c ├── usbd.h ├── video_fb.h ├── video_font_large.h ├── vsprintf.c └── vsprintf.h /Makefile: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE ?= ~/src/rpi3/gcc-linaro-5.5.0-2017.10-i686_aarch64-linux-gnu/bin/aarch64-linux-gnu- 2 | 3 | CC = $(CROSS_COMPILE)gcc 4 | LD = $(CROSS_COMPILE)ld 5 | OBJCOPY = $(CROSS_COMPILE)objcopy 6 | 7 | TARGET = shieldTV_loader 8 | TEXT_BASE = 0x0 9 | 10 | CFLAGS = \ 11 | -march=armv8-a \ 12 | -mlittle-endian \ 13 | -fno-stack-protector \ 14 | -mgeneral-regs-only \ 15 | -mstrict-align \ 16 | -fno-common \ 17 | -fno-builtin \ 18 | -ffreestanding \ 19 | -std=gnu99 \ 20 | -Werror \ 21 | -Wall \ 22 | -I ./ 23 | 24 | LDFLAGS = -pie 25 | 26 | %.o: %.S 27 | $(CC) $(CFLAGS) $< -c -o $@ 28 | 29 | %.o: %.c 30 | $(CC) $(CFLAGS) $< -c -o $@ 31 | 32 | all: $(TARGET) 33 | 34 | $(TARGET): $(TARGET).bin 35 | mkbootimg --pagesize 4096 --kernel $< --ramdisk /dev/null -o $@ 36 | 37 | $(TARGET).bin: $(TARGET).elf 38 | $(OBJCOPY) -v -O binary $< $@ 39 | 40 | $(TARGET).elf: start.o main.o string.o fdt.o ctype.o fdt_ro.o fdt_strerror.o vsprintf.o cfb_console.o usbd.o lib.o fb.o lmb.o 41 | $(LD) -T boot.lds -Ttext=$(TEXT_BASE) $(LDFLAGS) $^ -o $@ 42 | 43 | clean: 44 | rm -f *.o $(TARGET) $(TARGET).* *~ 45 | 46 | .PHONY: clean 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nVidia Shield TV fastboot bootloader. 2 | ===================================== 3 | 4 | Last updated December 5th, 2018. 5 | 6 | The Shield TV is based on the 64-bit nVidia X1 chip, and allows performing an 7 | unlock via "fastboot oem unlock", allowing custom OS images to be booted. 8 | 9 | It's all very tedious, though. You have to press hold the (only) touch 10 | button on the case for about 9 secs after applying power. You have to 11 | use `fastboot flash`, as `fastboot boot` is kinda broken on newer firmware. 12 | Then you have to `fastboot reboot`. 13 | 14 | We can do better than that! 15 | 16 | # Purpose 17 | 18 | A fastboot implementation to make development less painful. You can flash it 19 | to your boot partition, and on every power cycle it will patiently wait for 20 | you to send another image to run... or... do other things to it. 21 | 22 | It allows booting images over USB or exploring the system. It does not 23 | support flashing images. The regular NV fastboot implementation already 24 | does that. 25 | 26 | # Requirements 27 | 28 | * A Shield TV, unlocked. Search Youtube for walkthroughs. 29 | * Shield TV firmware version >= 1.3. 30 | * GNU Make. 31 | * An AArch64 GNU toolchain. 32 | * ADB/fastboot tools. 33 | * Bootimg tools (https://github.com/pbatard/bootimg-tools), built and somewhere in your path. 34 | * An HDMI-capable screen, capable of 1920x1080. 35 | 36 | # How to build 37 | 38 | 1. `$ CROSS_COMPILE=aarch64-linux-gnu- make` should give you `shieldTV_loader`. 39 | 40 | 2. After getting to the NV fastboot menu (either via `adb reboot-bootloader` or with the manual method), use `fastboot flash boot shieldTV_loader` to replace your boot partition. 41 | 42 | **NOTE: Until you reflash the Android boot partition, there's no more Shield OS.** 43 | 44 | 3. Power cycle (or `fastboot reboot`). 45 | 46 | # Commands 47 | 48 | - `flash run` will boot a binary or bootimg-wrapped binary image of your choice. If using mkbootimg-wrapped images, make sure `pagesize` corresponds to actual image alignment. Also, your image will be loaded at the first opportune place, so it better be position-independent. 49 | 50 | ``` 51 | $ fastboot flash run your_binary_image 52 | $ fastboot flash run your_mkbootimg_wrapped_binary_image 53 | ``` 54 | 55 | - `oem peek` is a pretty useful hexdumper. 56 | 57 | ``` 58 | $ fastboot oem peek 59 | $ fastboot oem peek 0x1000 8 8 60 | 61 | (bootloader) 00000000a00002e9 0000000000100008 0000000000100010 62 | (bootloader) 0000000000100018 0000000000000000 0000000000000000 63 | (bootloader) 0000000000000000 0000000000000000 64 | ``` 65 | 66 | - `oem poke` is pretty straightforward, too. 67 | 68 | ``` 69 | $ fastboot oem poke 0x80070000 c hello there 70 | $ fastboot oem peek 0x80070000 c 16 71 | 72 | (bootloader) h e l l o 20 t h e r e 00 04 00 00 00 73 | 74 | $ fastboot oem poke 0x80070000 4 1 2 3 4 75 | $ fastboot oem peek 0x80070000 4 4 76 | 77 | (bootloader) 00000001 00000002 00000003 00000004 78 | ``` 79 | 80 | - `oem echo` prints to the video screen with that sweet Sun OpenBoot font. 81 | 82 | ``` 83 | $ fastboot oem echo hello there 84 | ``` 85 | 86 | - `oem alloc` allocates a chunk of memory given parameters. 87 | 88 | ``` 89 | $ fastboot oem alloc 90 | ``` 91 | 92 | - 'oem alloc32' allocates a chunk of memory below 4GB. 93 | 94 | ``` 95 | $ fastboot oem alloc32 96 | ``` 97 | 98 | - 'oem free' frees a previous allocated chunk. 99 | 100 | ``` 101 | $ fastboot oem alloc32 0x100 4096 102 | 103 | (bootloader) 0xfd1fd000 104 | 105 | $ fastboot oem free 0xfd1fd000 0x100 4096 106 | 107 | $ fastboot oem alloc32 0x100 4096 108 | 109 | (bootloader) 0xfd1fd000 110 | ``` 111 | 112 | - `oem smccc` runs arbitrary ARM SMC commands, following the ARM SMCCC, so you can see how terrible the PSCI implementation really is. Maximum 8 parameters. Returns the four potential return values. 113 | 114 | ``` 115 | $ fastboot oem smccc 0 116 | 117 | (bootloader) 0xdeadbeef 0x0 0x0 0x0 118 | ``` 119 | 120 | - `oem reboot` resets the Shield. Argument can be one of "normal", "bootloader", "rcm", "recovery" or a PMC SCRATCH0 value if you're feeling brave. The regular reboot commands are also supported. 121 | 122 | ``` 123 | $ fastboot reboot 124 | $ fastboot reboot-bootloader 125 | $ fastboot oem reboot bootloader 126 | ``` 127 | 128 | # Contact 129 | 130 | Andrey Warkentin 131 | -------------------------------------------------------------------------------- /arm_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Various useful macros. 3 | * 4 | * Copyright (C) 2018 Andrei Warkentin 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #ifndef ARM_DEFS_H 22 | #define ARM_DEFS_H 23 | 24 | #define ReadSysReg(var, reg) asm volatile("mrs %0, " #reg : "=r" (var)) 25 | #define WriteSysReg(reg, val) asm volatile("msr " #reg ", %0" : : "r" (val)) 26 | #define ATS12E1R(what) asm volatile("at s12e1r, %0" : :"r" (what)) 27 | 28 | #define ISB() asm volatile("isb"); 29 | #define DSB_LD() asm volatile("dsb ld"); 30 | #define DSB_ST() asm volatile("dsb st"); 31 | #define DSB_ISH() asm volatile("dsb ish"); 32 | 33 | #define SPSR_2_EL(spsr) (X((spsr), 2, 3)) 34 | #define SPSR_2_BITNESS(spsr) (X((spsr), 4, 4) ? 32 : 64) 35 | #define SPSR_2_SPSEL(spsr) (X((spsr), 0, 0) ? 1 : 0) 36 | 37 | #define HPFAR_2_GPA(hpfar, far) (I(X((hpfar), 4, 39), 12, 47) | \ 38 | I((far), 11, 0)) 39 | #define ESR_EC_HVC64 0x16 40 | #define ESR_EC_SMC64 0x17 41 | #define ESR_EC_MSR 0x18 42 | #define ESR_EC_IABT_LO 0x20 43 | #define ESR_EC_DABT_LO 0x24 44 | #define ESR_EC_BRK 0x3C 45 | #define ESR_2_IL(x) (X((x), 25, 25)) 46 | #define ESR_2_EC(x) (X((x), 26, 31)) 47 | #define ESR_2_ISS(x) (X((x), 0, 24)) 48 | 49 | #define ISS_SYS_WRITE(x) ((X((x), 0, 0)) == 0) 50 | #define ISS_SYS_CRm(x) (X((x), 4, 1)) 51 | #define ISS_SYS_Rt(x) (X((x), 9, 5)) 52 | #define ISS_SYS_CRn(x) (X((x), 13, 10)) 53 | #define ISS_SYS_Op1(x) (X((x), 16, 14)) 54 | #define ISS_SYS_Op2(x) (X((x), 19, 17)) 55 | #define ISS_SYS_Op0(x) (X((x), 21, 20)) 56 | #define ISS_SYS_MSR(x) (X(ISS_SYS_Op0((x)), 1, 1) == 1) 57 | #define ISS_SYS_O0(x) X(ISS_SYS_Op0((x)), 0, 0) 58 | 59 | /* 60 | * Cook an ISS MSR access field into a common form, 61 | * where we saturate the access and register fields into 62 | * to known values. 63 | */ 64 | #define ISS_SYS_2_MSRDEF(x) ((x) | I(1, 0, 0) | I(0x1F, 9, 5)) 65 | #define MSRDEF(O0, Op1, CRn, CRm, Op2) \ 66 | ISS_SYS_2_MSRDEF(I(CRm, 4, 1) | I(CRn, 13, 10) | \ 67 | I(Op1, 16, 14) | I(Op2, 19, 17) | \ 68 | I(1, 21, 21) | I(O0, 20, 20)) 69 | 70 | #define MSRDEF_OSDTRRX_EL1 MSRDEF(0, 0, 0, 0, 2) 71 | #define MSRDEF_MDCCINT_EL1 MSRDEF(0, 0, 0, 2, 0) 72 | #define MSRDEF_MDSCR_EL1 MSRDEF(0, 0, 0, 2, 2) 73 | #define MSRDEF_OSDTRTX_EL1 MSRDEF(0, 0, 0, 3, 2) 74 | #define MSRDEF_OSECCR_EL1 MSRDEF(0, 0, 0, 6, 2) 75 | #define MSRDEF_DBGBVR_EL1(x) MSRDEF(0, 0, 0, (x), 4) 76 | #define MSRDEF_DBGBCR_EL1(x) MSRDEF(0, 0, 0, (x), 5) 77 | #define MSRDEF_DBGWVR_EL1(x) MSRDEF(0, 0, 0, (x), 6) 78 | #define MSRDEF_DBGWCR_EL1(x) MSRDEF(0, 0, 0, (x), 7) 79 | #define MSRDEF_MDRAR_EL1 MSRDEF(0, 0, 1, 0, 0) 80 | #define MSRDEF_OSLAR_EL1 MSRDEF(0, 0, 1, 0, 4) 81 | #define MSRDEF_OSLSR_EL1 MSRDEF(0, 0, 1, 1, 4) 82 | #define MSRDEF_OSDLR_EL1 MSRDEF(0, 0, 1, 3, 4) 83 | #define MSRDEF_DBGPRCR_EL1 MSRDEF(0, 0, 1, 4, 4) 84 | #define MSRDEF_DBGCLAIMSET_EL1 MSRDEF(0, 0, 7, 8, 6) 85 | #define MSRDEF_DBGCLAIMCLR_EL1 MSRDEF(0, 0, 7, 9, 6) 86 | #define MSRDEF_DBGAUTHSTAT_EL1 MSRDEF(0, 0, 7, 14, 6) 87 | #define MSRDEF_MDCCSR_EL0 MSRDEF(0, 3, 0, 1, 0) 88 | #define MSRDEF_DBGDTR_EL0 MSRDEF(0, 3, 0, 4, 0) 89 | #define MSRDEF_DBGDTRF_EL0 MSRDEF(0, 3, 0, 5, 0) 90 | 91 | #define MDCR_TDE BIT(8) 92 | #define HCR_VM BIT(0) 93 | #define HCR_AMO BIT(5) 94 | #define HCR_VSE BIT(8) 95 | #define HCR_TSC BIT(19) 96 | #define HCR_RW_64 BIT(31) 97 | #define SPSR_D BIT(9) 98 | #define SPSR_EL1 0x4 99 | #define SPSR_ELx 0x1 100 | 101 | #define SCTLR_M BIT(0) 102 | #define SCTLR_A BIT(1) 103 | #define SCTLR_C BIT(2) 104 | #define SCTLR_SA BIT(3) 105 | #define SCTLR_I BIT(12) 106 | #define SCTLR_EL1_RES1 (BIT(29) | BIT(28) | BIT(23) | \ 107 | BIT(22) | BIT(20) | BIT(11)) 108 | /* 109 | * Device is GRE (to allow EL1 to do whatever). 110 | * Memory is Inner-WB Outer-WB. 111 | */ 112 | #define PTE_S2_ATTR_MEM (I(3, 4, 5) | I(3, 2, 3)) 113 | #define PTE_S2_ATTR_DEV (I(0, 4, 5) | I(3, 2, 3)) 114 | #define PTE_RW I(0x1, 6, 7) 115 | #define PTE_S2_RW I(0x3, 6, 7) 116 | #define PTE_S2_RO I(0x1, 6, 7) 117 | #define PTE_SH_INNER I(0x3, 8, 9) 118 | #define PTE_AF I(0x1, 10, 10) 119 | 120 | #define PTE_TYPE_TAB 0x3 121 | #define PTE_TYPE_BLOCK 0x1 122 | #define PTE_TYPE_BLOCK_L3 0x3 123 | #define PTE_2_TYPE(x) ((x) & 0x3) 124 | /* 125 | * Assumes 4K granularity. 126 | */ 127 | #define PTE_2_TAB(x) ((VOID *)M((x), 47, 12)) 128 | #define VA_2_PL1_IX(x) X((x), 30, 38) 129 | #define VA_2_PL2_IX(x) X((x), 21, 29) 130 | #define VA_2_PL3_IX(x) X((x), 20, 12) 131 | 132 | #define PAR_IS_BAD(par) (((par) & 1) == 1) 133 | #define PAR_2_ADDR(par) M((par), 47, 12) 134 | 135 | #define TLBI_S12() do { \ 136 | ISB(); \ 137 | asm volatile("tlbi vmalls12e1"); \ 138 | DSB_ISH(); \ 139 | ISB(); \ 140 | } while (0) 141 | 142 | #define PSCI_CPU_ON_64 0xC4000003 143 | #define PSCI_CPU_OFF 0x84000002 144 | #define PSCI_RETURN_STATUS_SUCCESS 0 145 | #define PSCI_RETURN_STATUS_DENIED -3 146 | #define PSCI_RETURN_INTERNAL_FAILURE -6 147 | 148 | static inline uint8_t 149 | IN8(const volatile void *addr) 150 | { 151 | uint8_t val; 152 | asm volatile("ldrb %w0, [%1]" : "=r" (val) : "r" (addr)); 153 | DSB_LD(); 154 | return val; 155 | } 156 | 157 | static inline uint16_t 158 | _IN16(const volatile void *addr) 159 | { 160 | uint16_t val; 161 | asm volatile("ldrh %w0, [%1]" : "=r" (val) : "r" (addr)); 162 | DSB_LD(); 163 | return val; 164 | } 165 | #define IN16(x) _IN16(VP(x)) 166 | 167 | static inline uint32_t 168 | _IN32(const volatile void *addr) 169 | { 170 | uint32_t val; 171 | asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (addr)); 172 | DSB_LD(); 173 | return val; 174 | } 175 | #define IN32(x) _IN32(VP(x)) 176 | 177 | static inline uint64_t 178 | _IN64(const volatile void *addr) 179 | { 180 | uint64_t val; 181 | asm volatile("ldr %x0, [%1]" : "=r" (val) : "r" (addr)); 182 | DSB_LD(); 183 | return val; 184 | } 185 | #define IN64(x) _IN64(VP(x)) 186 | 187 | static inline void 188 | _OUT8(uint8_t val, volatile void *addr) 189 | { 190 | DSB_ST(); 191 | asm volatile("strb %w0, [%1]" : : "r" (val), "r" (addr)); 192 | } 193 | #define OUT8(val, addr) _OUT8((uint8_t) (val), VP(addr)) 194 | 195 | static inline void 196 | _OUT16(uint16_t val, volatile void *addr) 197 | { 198 | DSB_ST(); 199 | asm volatile("strh %w0, [%1]" : : "r" (val), "r" (addr)); 200 | } 201 | #define OUT16(val, addr) _OUT16((uint16_t) (val), VP(addr)) 202 | 203 | static inline void 204 | _OUT32(uint32_t val, volatile void *addr) 205 | { 206 | DSB_ST(); 207 | asm volatile("str %w0, [%1]" : : "r" (val), "r" (addr)); 208 | } 209 | #define OUT32(val, addr) _OUT32((uint32_t) (val), VP(addr)) 210 | 211 | static inline void 212 | _OUT64(uint64_t val, volatile void *addr) 213 | { 214 | DSB_ST(); 215 | asm volatile("str %x0, [%1]" : : "r" (val), "r" (addr)); 216 | } 217 | #define OUT64(val, addr) _OUT64((uint64_t) (val), VP(addr)) 218 | 219 | #endif /* ARM_DEFS_H */ 220 | -------------------------------------------------------------------------------- /boot.lds: -------------------------------------------------------------------------------- 1 | /* 2 | * Andrei Warkentin (C) 2014 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | OUTPUT_FORMAT("elf64-littleaarch64") 21 | OUTPUT_ARCH(aarch64) 22 | ENTRY(asm_start) 23 | 24 | SECTIONS 25 | { 26 | .text 0x0 : ALIGN(CONSTANT(COMMONPAGESIZE)) { 27 | PROVIDE(image_start = .); 28 | start.o (.text) 29 | *(.text .text*) 30 | *(.got .got*) 31 | *(.rodata .rodata*) 32 | *(.data .data*) 33 | . = ALIGN(0x20); 34 | PROVIDE(bss_start = .); 35 | *(.bss .bss*) 36 | PROVIDE(bss_end = .); 37 | 38 | . = ALIGN(0x20); 39 | PROVIDE(rela_start = .); 40 | *(.rela .rela*) 41 | PROVIDE(rela_end = .); 42 | PROVIDE(image_end = . ); 43 | } 44 | 45 | /DISCARD/ : { 46 | *(.note.GNU-stack) 47 | *(.gnu_debuglink) 48 | *(.interp) 49 | *(.dynamic) 50 | *(.dynsym) 51 | *(.dynstr) 52 | *(.hash) 53 | *(.comment) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ctype.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Andrei Warkentin 3 | * 4 | * This program is free software ; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | /* 10 | * Copyright (C) 1991, 1992 Linus Torvalds 11 | */ 12 | 13 | #include 14 | 15 | const unsigned char _ctype[] = { 16 | _C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ 17 | _C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ 18 | _C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ 19 | _C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ 20 | _S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ 21 | _P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ 22 | _D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ 23 | _D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ 24 | _P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ 25 | _U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ 26 | _U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ 27 | _U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ 28 | _P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ 29 | _L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ 30 | _L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ 31 | _L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ 32 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ 33 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ 34 | _S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ 35 | _P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ 36 | _U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ 37 | _U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ 38 | _L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ 39 | _L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ 40 | -------------------------------------------------------------------------------- /ctype.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Andrei Warkentin 3 | * 4 | * This program is free software ; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | #ifndef CTYPE_H 10 | #define CTYPE_H 11 | 12 | /* 13 | * NOTE! This ctype does not handle EOF like the standard C 14 | * library is required to. 15 | */ 16 | 17 | #define _U 0x01 /* upper */ 18 | #define _L 0x02 /* lower */ 19 | #define _D 0x04 /* digit */ 20 | #define _C 0x08 /* cntrl */ 21 | #define _P 0x10 /* punct */ 22 | #define _S 0x20 /* white space (space/lf/tab) */ 23 | #define _X 0x40 /* hex digit */ 24 | #define _SP 0x80 /* hard space (0x20) */ 25 | 26 | extern const unsigned char _ctype[]; 27 | 28 | #define __ismask(x) (_ctype[(int)(unsigned char)(x)]) 29 | 30 | #define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) 31 | #define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) 32 | #define iscntrl(c) ((__ismask(c)&(_C)) != 0) 33 | #define isdigit(c) ((__ismask(c)&(_D)) != 0) 34 | #define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) 35 | #define islower(c) ((__ismask(c)&(_L)) != 0) 36 | #define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) 37 | #define ispunct(c) ((__ismask(c)&(_P)) != 0) 38 | /* Note: isspace() must return false for %NUL-terminator */ 39 | #define isspace(c) ((__ismask(c)&(_S)) != 0) 40 | #define isupper(c) ((__ismask(c)&(_U)) != 0) 41 | #define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) 42 | 43 | #define isascii(c) (((unsigned char)(c))<=0x7f) 44 | #define toascii(c) (((unsigned char)(c))&0x7f) 45 | 46 | static inline unsigned char __tolower(unsigned char c) 47 | { 48 | if (isupper(c)) 49 | c -= 'A'-'a'; 50 | return c; 51 | } 52 | 53 | static inline unsigned char __toupper(unsigned char c) 54 | { 55 | if (islower(c)) 56 | c -= 'a'-'A'; 57 | return c; 58 | } 59 | 60 | #define tolower(c) __tolower(c) 61 | #define toupper(c) __toupper(c) 62 | 63 | #endif /* CTYPE_H */ 64 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Various useful macros. 3 | * 4 | * Copyright (C) 2015 Andrei Warkentin 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #ifndef DEFS_H 22 | #define DEFS_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | typedef uint64_t size_t; 29 | typedef uint64_t ptrdiff_t; 30 | typedef _Bool bool_t; 31 | typedef uint64_t phys_addr_t; 32 | 33 | #define min(x,y) ({ \ 34 | typeof(x) _x = (x); \ 35 | typeof(y) _y = (y); \ 36 | (void) (&_x == &_y); \ 37 | _x < _y ? _x : _y; }) 38 | 39 | #define max(x,y) ({ \ 40 | typeof(x) _x = (x); \ 41 | typeof(y) _y = (y); \ 42 | (void) (&_x == &_y); \ 43 | _x > _y ? _x : _y; }) 44 | 45 | #define NULL ((void *) 0) 46 | #define BITS_PER_LONG 64 47 | #define PAGE_SIZE 4096 48 | 49 | #define BIT(x) (1ull << (x)) 50 | 51 | #define container_of(ptr, type, member) ({ \ 52 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 53 | (type *)( (char *)__mptr - offsetof(type,member) );}) 54 | 55 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 56 | 57 | #define USHRT_MAX ((uint16_t)(~0U)) 58 | #define SHRT_MAX ((int16_t)(USHRT_MAX>>1)) 59 | #define SHRT_MIN ((int16_t)(-SHRT_MAX - 1)) 60 | #define INT_MAX ((int)(~0U>>1)) 61 | #define INT_MIN (-INT_MAX - 1) 62 | #define UINT_MAX (~0U) 63 | #define LONG_MAX ((long)(~0UL>>1)) 64 | #define LONG_MIN (-LONG_MAX - 1) 65 | #define ULONG_MAX (~0UL) 66 | 67 | #define likely(x) (__builtin_constant_p(x) ? !!(x) : __builtin_expect(!!(x), 1)) 68 | #define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __builtin_expect(!!(x), 0)) 69 | 70 | #define MB(x) (1UL * x * 1024 * 1024) 71 | #define TB(x) (1UL * x * 1024 * 1024 * 1024 * 1024) 72 | 73 | #define __packed __attribute__((packed)) 74 | #define __align(x) __attribute__((__aligned__(x))) 75 | #define __unused __attribute__((unused)) 76 | #define __used __attribute__((used)) 77 | #define __section(x) __attribute__((__section__(x))) 78 | #define __noreturn __attribute__((noreturn)) 79 | #define __attrconst __attribute__((const)) 80 | #define __warn_unused_result __attribute__((warn_unused_result)) 81 | #define __alwaysinline __attribute__((always_inline)) 82 | 83 | #define do_div(n,base) ({\ 84 | uint32_t __base = (base);\ 85 | uint32_t __rem;\ 86 | __rem = ((uint64_t)(n)) % __base;\ 87 | (n) = ((uint64_t)(n)) / __base;\ 88 | __rem;\ 89 | }) 90 | 91 | static inline uint16_t 92 | swab16(uint16_t x) 93 | { 94 | return ((x & (uint16_t)0x00ffU) << 8) | 95 | ((x & (uint16_t)0xff00U) >> 8); 96 | } 97 | 98 | static inline uint32_t 99 | swab32(uint32_t x) 100 | { 101 | return ((x & (uint32_t)0x000000ffUL) << 24) | 102 | ((x & (uint32_t)0x0000ff00UL) << 8) | 103 | ((x & (uint32_t)0x00ff0000UL) >> 8) | 104 | ((x & (uint32_t)0xff000000UL) >> 24); 105 | } 106 | 107 | static inline uint64_t 108 | swab64(uint64_t x) 109 | { 110 | return (uint64_t)((x & (uint64_t)0x00000000000000ffULL) << 56) | 111 | (uint64_t)((x & (uint64_t)0x000000000000ff00ULL) << 40) | 112 | (uint64_t)((x & (uint64_t)0x0000000000ff0000ULL) << 24) | 113 | (uint64_t)((x & (uint64_t)0x00000000ff000000ULL) << 8) | 114 | (uint64_t)((x & (uint64_t)0x000000ff00000000ULL) >> 8) | 115 | (uint64_t)((x & (uint64_t)0x0000ff0000000000ULL) >> 24) | 116 | (uint64_t)((x & (uint64_t)0x00ff000000000000ULL) >> 40) | 117 | (uint64_t)((x & (uint64_t)0xff00000000000000ULL) >> 56); 118 | } 119 | 120 | #define cpu_to_be64(x) swab64(x) 121 | #define cpu_to_be32(x) swab32(x) 122 | #define cpu_to_be16(x) swab16(x) 123 | #define cpu_to_le64(x) ((uint64_t) x) 124 | #define cpu_to_le32(x) ((uint32_t) x) 125 | #define cpu_to_le16(x) ((uint16_t) x) 126 | #define be64_to_cpu(x) swab64(x) 127 | #define be32_to_cpu(x) swab32(x) 128 | #define be16_to_cpu(x) swab16(x) 129 | #define le64_to_cpu(x) ((uint64_t) x) 130 | #define le32_to_cpu(x) ((uint32_t) x) 131 | #define le16_to_cpu(x) ((uint16_t) x) 132 | 133 | #define _IX_BITS(sm, bg) ((bg) - (sm) + 1) 134 | #define _IX_MASK(sm, bg) ((1ul << _IX_BITS((sm), (bg))) - 1) 135 | #define _INTERNAL_X(val, sm, bg) ((val) >> (sm)) & _IX_MASK((sm), (bg)) 136 | #define X(val, ix1, ix2) (((ix1) < (ix2)) ? _INTERNAL_X((val), (ix1), (ix2)) : \ 137 | _INTERNAL_X((val), (ix2), (ix1))) 138 | 139 | #define _INTERNAL_I(val, sm, bg) (((val) & _IX_MASK((sm), (bg))) << (sm)) 140 | #define I(val, ix1, ix2) (((ix1) < (ix2)) ? _INTERNAL_I((val), (ix1), (ix2)) : \ 141 | _INTERNAL_I((val), (ix2), (ix1))) 142 | #define _INTERNAL_M(val, sm, bg) ((val) & (_IX_MASK((sm), (bg)) << (sm))) 143 | #define M(val, ix1, ix2) (((ix1) < (ix2)) ? _INTERNAL_M((val), (ix1), (ix2)) : \ 144 | _INTERNAL_M((val), (ix2), (ix1))) 145 | 146 | #define A_UP(Value, Alignment) (((Value) + (Alignment) - 1) & (~((Alignment) - 1))) 147 | #define PA_UP(p, align) ((typeof(p)) A_UP(((uintptr_t) (p)), align)) 148 | #define A_DOWN(Value, Alignment) ((Value) & (~((Alignment) - 1))) 149 | 150 | #define IS_ALIGNED(Value, Alignment) (((UINTN) (Value) & (Alignment - 1)) == 0) 151 | 152 | #define VP(x) ((void *)(x)) 153 | #define U8P(x) ((uint8_t *)(x)) 154 | #define U16P(x) ((uint16_t *)(x)) 155 | #define U32P(x) ((uint32_t *)(x)) 156 | #define U64P(x) ((uint64_t *)(x)) 157 | #define UN(x) ((uintptr_t)(x)) 158 | 159 | #define ELES(x) (sizeof((x)) / sizeof((x)[0])) 160 | 161 | #define _INTERNAL_S(x) #x 162 | #define S(x) _INTERNAL_S(x) 163 | 164 | #define C_ASSERT(e) { typedef char __C_ASSERT__[(e)?0:-1]; __C_ASSERT__ c; (void)c; } 165 | 166 | #endif /* DEFS_H */ 167 | -------------------------------------------------------------------------------- /fb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | /* 21 | * Gonna keep this here but I don't really think there's anything 22 | * here from any Google fastboot implementation, lol: 23 | * 24 | * Copyright (c) 2008, Google Inc. 25 | * All rights reserved. 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions 29 | * are met: 30 | * * Redistributions of source code must retain the above copyright 31 | * notice, this list of conditions and the following disclaimer. 32 | * * Redistributions in binary form must reproduce the above copyright 33 | * notice, this list of conditions and the following disclaimer in 34 | * the documentation and/or other materials provided with the 35 | * distribution. 36 | * 37 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 38 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 39 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 40 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 41 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 42 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 43 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 44 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 45 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 | * SUCH DAMAGE. 49 | */ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #define DOWNLOAD_ALIGNMENT 0x100000 58 | 59 | #define BOOT_MAGIC "ANDROID!" 60 | #define BOOT_MAGIC_SIZE 8 61 | #define BOOT_NAME_SIZE 16 62 | #define BOOT_ARGS_SIZE 512 63 | #define BOOT_EXTRA_ARGS_SIZE 1024 64 | 65 | #define FB_BAD_COMMAND "FAILBad command" 66 | #define FB_UNKNOWN_COMMAND "FAILUnknown command" 67 | #define FB_OOM "FAILOut of memory" 68 | #define FB_NOT_DOWNLOADED "FAILNothing downloaded" 69 | 70 | #define FB_OK NULL 71 | 72 | typedef struct boot_img { 73 | unsigned char magic[BOOT_MAGIC_SIZE]; 74 | unsigned kernel_size; /* size in bytes */ 75 | unsigned kernel_addr; /* physical load addr */ 76 | unsigned ramdisk_size; /* size in bytes */ 77 | unsigned ramdisk_addr; /* physical load addr */ 78 | unsigned second_size; /* size in bytes */ 79 | unsigned second_addr; /* physical load addr */ 80 | unsigned tags_addr; /* physical addr for kernel tags */ 81 | unsigned page_size; /* flash page size we assume */ 82 | unsigned unused[2]; /* future expansion: should be 0 */ 83 | unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ 84 | unsigned char cmdline[BOOT_ARGS_SIZE]; 85 | unsigned id[8]; /* timestamp / checksum / sha1 / etc */ 86 | /* Supplemental command line data; kept here to maintain 87 | * binary compatibility with older versions of mkbootimg */ 88 | unsigned char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; 89 | } boot_img; 90 | 91 | typedef char *fb_status; 92 | 93 | typedef struct fb_reboot_state { 94 | reboot_type type; 95 | } fb_reboot_state; 96 | 97 | typedef struct fb_peek_state { 98 | phys_addr_t addr; 99 | size_t access; 100 | size_t items; 101 | bool_t ascii; 102 | char *buffer; 103 | size_t buffer_len; 104 | } fb_peek_state; 105 | 106 | typedef struct fb_mem { 107 | /* 108 | * 2 per EP0, 2 per EP1. 109 | */ 110 | usbd_td qtds[4]; 111 | usbd uctx; 112 | usbd_req ep1_out_req; 113 | usbd_req ep1_in_req; 114 | bool_t in_command; 115 | uint8_t *last_loaded; 116 | size_t load_size; 117 | size_t load_rem; 118 | size_t load_align; 119 | /* 120 | * Command states. 121 | */ 122 | union { 123 | fb_peek_state peek; 124 | fb_reboot_state reboot; 125 | }; 126 | void *fdt; 127 | } fb_mem; 128 | 129 | static usbd_ep fb_ep1_out = { 130 | .num = 1, 131 | .send = false, 132 | .type = EP_TYPE_BULK, 133 | }; 134 | 135 | static usbd_ep fb_ep1_in = { 136 | .num = 1, 137 | .send = true, 138 | .type = EP_TYPE_BULK, 139 | }; 140 | 141 | static usbd_ep *fb_eps[] = { 142 | &fb_ep1_out, 143 | &fb_ep1_in, 144 | NULL 145 | }; 146 | 147 | static void fb_rx_data(struct usbd *context, 148 | usbd_req *unused); 149 | 150 | static void fb_rx_cmd(struct usbd *context, 151 | usbd_req *unused); 152 | 153 | 154 | static void 155 | fb_end_command_complete(usbd *context, 156 | usbd_req *req) 157 | { 158 | fb_mem *fb = context->ctx; 159 | fb->in_command = false; 160 | } 161 | 162 | static void 163 | fb_end_command_with_custom_complete(struct usbd *context, 164 | char *status, 165 | void (*complete)(usbd *, usbd_req *)) 166 | { 167 | fb_mem *fb = context->ctx; 168 | 169 | if (status == FB_OK) { 170 | status = "OKAY"; 171 | } 172 | 173 | fb->ep1_in_req.buffer = fb->ep1_in_req.small_buffer; 174 | fb->ep1_in_req.buffer_length = strlen(status) + 1; 175 | BUG_ON (fb->ep1_in_req.buffer_length > 176 | sizeof(fb->ep1_in_req.small_buffer)); 177 | memcpy(fb->ep1_in_req.buffer, status, strlen(status) + 1); 178 | fb->ep1_in_req.complete = complete; 179 | usbd_req_submit(context, &(fb->ep1_in_req)); 180 | } 181 | 182 | static void 183 | fb_end_command(struct usbd *context, 184 | char *status) 185 | { 186 | fb_end_command_with_custom_complete(context, status, 187 | fb_end_command_complete); 188 | } 189 | 190 | static void 191 | fb_end_command_with_info_complete(usbd *context, 192 | usbd_req *req) 193 | { 194 | fb_end_command(context, FB_OK); 195 | } 196 | 197 | static void 198 | fb_end_command_with_info(struct usbd *context, char *fmt, ...) 199 | { 200 | va_list list; 201 | fb_mem *fb = context->ctx; 202 | 203 | va_start(list, fmt); 204 | fb->ep1_in_req.buffer = fb->ep1_in_req.small_buffer; 205 | 206 | memcpy(fb->ep1_in_req.buffer, "INFO", 4); 207 | fb->ep1_in_req.buffer_length = 208 | vscnprintf(fb->ep1_in_req.buffer + 4, 209 | sizeof(fb->ep1_in_req.small_buffer) - 4, 210 | fmt, list) + 4; 211 | 212 | fb->ep1_in_req.complete = fb_end_command_with_info_complete; 213 | usbd_req_submit(context, &(fb->ep1_in_req)); 214 | 215 | va_end(list); 216 | } 217 | 218 | static void 219 | fb_request_data(struct usbd *context) 220 | { 221 | fb_mem *fb = context->ctx; 222 | 223 | fb->ep1_in_req.buffer = fb->ep1_in_req.small_buffer; 224 | 225 | fb->ep1_in_req.buffer_length = 226 | scnprintf(fb->ep1_in_req.buffer, 227 | sizeof(fb->ep1_in_req.small_buffer), 228 | "DATA%08x", fb->load_size); 229 | 230 | fb->ep1_in_req.complete = NULL; 231 | usbd_req_submit(context, &(fb->ep1_in_req)); 232 | } 233 | 234 | static void 235 | fb_oem_cmd_peek_exe(usbd *context, 236 | usbd_req *req) 237 | { 238 | fb_mem *fb = context->ctx; 239 | fb_peek_state *peek = &(fb->peek); 240 | char *b = peek->buffer; 241 | 242 | if (req != NULL && req->error) { 243 | return; 244 | } 245 | 246 | /* 247 | * Buffer starts with INFO, ends with NUL. 248 | */ 249 | size_t len_to_fill = peek->buffer_len - 4 - 1; 250 | /* 251 | * Each item is hex followed by white space. If doing 252 | * ascii, printable characters are aligned to the right. 253 | */ 254 | size_t item_sz = peek->access * 2 + 1; 255 | size_t items = len_to_fill / item_sz; 256 | 257 | items = min(items, peek->items); 258 | if (items == 0) { 259 | fb_end_command(context, FB_OK); 260 | return; 261 | } 262 | 263 | memcpy(b, "INFO", 4); 264 | b += 4; 265 | 266 | peek->items -= items; 267 | while (items--) { 268 | uint64_t value; 269 | 270 | if (peek->access == 1) { 271 | value = IN8(VP(peek->addr)); 272 | } else if (peek->access == 2) { 273 | value = IN16(VP(peek->addr)); 274 | } else if (peek->access == 4) { 275 | value = IN32(VP(peek->addr)); 276 | } else { 277 | value = IN64(VP(peek->addr)); 278 | } 279 | peek->addr += peek->access; 280 | 281 | if (peek->ascii && isascii(value) && isgraph(value)) { 282 | b += scnprintf(b, len_to_fill, " %c ", value); 283 | } else { 284 | b += scnprintf(b, len_to_fill, "%0*lx ", 285 | peek->access * 2, value); 286 | } 287 | len_to_fill -= item_sz; 288 | } 289 | 290 | *b++ = '\0'; 291 | BUG_ON (b - peek->buffer > peek->buffer_len); 292 | fb->ep1_in_req.buffer_length = b - peek->buffer; 293 | fb->ep1_in_req.complete = fb_oem_cmd_peek_exe; 294 | usbd_req_submit(context, &(fb->ep1_in_req)); 295 | } 296 | 297 | static fb_status 298 | fb_oem_cmd_peek(usbd *context, 299 | char *cmd) 300 | { 301 | fb_mem *fb = context->ctx; 302 | fb_peek_state *peek = &fb->peek; 303 | 304 | peek->addr = simple_strtoull(cmd, &cmd, 0); 305 | if (*cmd != ' ') { 306 | return FB_BAD_COMMAND; 307 | } 308 | cmd++; 309 | 310 | if (*cmd == 'c') { 311 | peek->access = 1; 312 | peek->ascii = true; 313 | cmd++; 314 | } else { 315 | peek->ascii = false; 316 | peek->access = simple_strtoull(cmd, &cmd, 0); 317 | if (peek->access != 1 && peek->access != 2 && 318 | peek->access != 4 && peek->access != 8) { 319 | return FB_BAD_COMMAND; 320 | } 321 | } 322 | 323 | peek->items = 1; 324 | if (*cmd == ' ') { 325 | cmd++; 326 | /* 327 | * Have items. 328 | */ 329 | peek->items = simple_strtoull(cmd, &cmd, 0); 330 | } 331 | 332 | if (*cmd != '\0') { 333 | return FB_BAD_COMMAND; 334 | } 335 | 336 | C_ASSERT(sizeof(fb->ep1_in_req.small_buffer) >= 64); 337 | peek->buffer = (char *) fb->ep1_in_req.small_buffer; 338 | peek->buffer_len = sizeof(fb->ep1_in_req.small_buffer); 339 | fb_oem_cmd_peek_exe(context, NULL); 340 | return FB_OK; 341 | } 342 | 343 | static fb_status 344 | fb_oem_cmd_poke(usbd *context, 345 | char *cmd) 346 | { 347 | size_t access; 348 | bool_t ascii; 349 | phys_addr_t addr; 350 | 351 | addr = simple_strtoull(cmd, &cmd, 0); 352 | if (*cmd != ' ') { 353 | return FB_BAD_COMMAND; 354 | } 355 | cmd++; 356 | 357 | if (*cmd == 'c') { 358 | access = 1; 359 | ascii = true; 360 | cmd++; 361 | } else { 362 | ascii = false; 363 | access = simple_strtoull(cmd, &cmd, 0); 364 | if (access != 1 && access != 2 && 365 | access != 4 && access != 8) { 366 | return FB_BAD_COMMAND; 367 | } 368 | } 369 | 370 | if (*cmd != ' ') { 371 | return FB_BAD_COMMAND; 372 | } 373 | cmd++; 374 | 375 | while (*cmd != '\0') { 376 | if (ascii) { 377 | OUT8(*(uint8_t *) cmd, addr); 378 | cmd++; 379 | } else { 380 | uint64_t value = simple_strtoull(cmd, &cmd, 0); 381 | if (*cmd == ' ') { 382 | cmd++; 383 | } else if (*cmd != '\0') { 384 | return FB_BAD_COMMAND; 385 | } 386 | 387 | if (access == 1) { 388 | OUT8((uint8_t) value, addr); 389 | } else if (access == 2) { 390 | OUT16((uint16_t) value, addr); 391 | } else if (access == 4) { 392 | OUT32((uint32_t) value, addr); 393 | } else if (access == 8) { 394 | OUT64((uint64_t) value, addr); 395 | } 396 | } 397 | 398 | addr += access; 399 | } 400 | 401 | fb_end_command(context, FB_OK); 402 | return FB_OK; 403 | } 404 | 405 | static fb_status 406 | fb_oem_cmd_echo(usbd *context, 407 | char *cmd) 408 | { 409 | printk("%s\n", cmd); 410 | fb_end_command(context, FB_OK); 411 | return FB_OK; 412 | } 413 | 414 | static fb_status 415 | fb_oem_cmd_alloc_ex(usbd *context, 416 | char *cmd, 417 | phys_addr_t max_addr) 418 | { 419 | size_t size; 420 | size_t align; 421 | lmb_type_t type; 422 | phys_addr_t addr; 423 | 424 | size = simple_strtoull(cmd, &cmd, 0); 425 | if (*cmd != ' ') { 426 | return FB_BAD_COMMAND; 427 | } 428 | cmd++; 429 | 430 | align = simple_strtoull(cmd, &cmd, 0); 431 | 432 | type = LMB_BOOT; 433 | if (*cmd == ' ') { 434 | cmd++; 435 | 436 | if (!memcmp(cmd, "boot", sizeof("boot"))) { 437 | type = LMB_BOOT; 438 | } else if (!memcmp(cmd, "runtime", sizeof("runtime"))) { 439 | /* 440 | * Today, there is no difference between boot and 441 | * runtime allocations. Tomorrow? We'll see. 442 | */ 443 | type = LMB_RUNTIME; 444 | } else { 445 | return FB_BAD_COMMAND; 446 | } 447 | } 448 | 449 | addr = lmb_alloc_base(&lmb, size, align, max_addr, 450 | type, LMB_TAG("FBRQ")); 451 | if (addr == 0) { 452 | return FB_OOM; 453 | } 454 | 455 | fb_end_command_with_info(context, "0x%lx", addr); 456 | return FB_OK; 457 | } 458 | 459 | static fb_status 460 | fb_oem_cmd_alloc(usbd *context, 461 | char *cmd) 462 | { 463 | return fb_oem_cmd_alloc_ex(context, cmd, LMB_ALLOC_ANYWHERE); 464 | } 465 | 466 | static fb_status 467 | fb_oem_cmd_alloc32(usbd *context, 468 | char *cmd) 469 | { 470 | return fb_oem_cmd_alloc_ex(context, cmd, LMB_ALLOC_32BIT); 471 | } 472 | 473 | static fb_status 474 | fb_oem_cmd_free(usbd *context, 475 | char *cmd) 476 | { 477 | phys_addr_t addr; 478 | size_t size; 479 | size_t align; 480 | 481 | addr = simple_strtoull(cmd, &cmd, 0); 482 | if (*cmd != ' ') { 483 | return FB_BAD_COMMAND; 484 | } 485 | cmd++; 486 | 487 | size = simple_strtoull(cmd, &cmd, 0); 488 | if (*cmd != ' ') { 489 | return FB_BAD_COMMAND; 490 | } 491 | cmd++; 492 | 493 | align = simple_strtoull(cmd, &cmd, 0); 494 | if (*cmd != '\0') { 495 | return FB_BAD_COMMAND; 496 | } 497 | 498 | lmb_free(&lmb, addr, size, align); 499 | 500 | fb_end_command(context, FB_OK); 501 | return FB_OK; 502 | } 503 | 504 | static fb_status 505 | fb_oem_cmd_smccc(usbd *context, 506 | char *cmd) 507 | { 508 | int i; 509 | uint64_t inout[8]; 510 | extern void smc_call(uint64_t *inout); 511 | 512 | memset(inout, 0, sizeof(inout)); 513 | for (i = 0; i < ELES(inout); i++) { 514 | inout[i] = simple_strtoull(cmd, &cmd, 0); 515 | if (*cmd == '\0') { 516 | break; 517 | } 518 | 519 | if (*cmd != ' ') { 520 | return FB_BAD_COMMAND; 521 | } 522 | cmd++; 523 | } 524 | 525 | if (*cmd != '\0') { 526 | return FB_BAD_COMMAND; 527 | } 528 | 529 | smc_call(inout); 530 | fb_end_command_with_info(context, "0x%lx 0x%lx 0x%lx 0x%lx", 531 | inout[0], inout[1], inout[2], inout[3]); 532 | 533 | return FB_OK; 534 | } 535 | 536 | static void 537 | fb_cmd_reboot_complete(usbd *context, 538 | usbd_req *req) 539 | { 540 | fb_mem *fb = context->ctx; 541 | usbd_fini(context); 542 | tegra_reboot(fb->reboot.type); 543 | } 544 | 545 | static fb_status 546 | fb_cmd_reboot(usbd *context, 547 | reboot_type type) 548 | { 549 | fb_mem *fb = context->ctx; 550 | fb->reboot.type = type; 551 | 552 | fb_end_command_with_custom_complete(context, FB_OK, 553 | fb_cmd_reboot_complete); 554 | 555 | return FB_OK; 556 | } 557 | 558 | static fb_status 559 | fb_oem_cmd_reboot(usbd *context, 560 | char *cmd) 561 | { 562 | uint32_t s; 563 | 564 | if (!strcmp(cmd, "normal")) { 565 | return fb_cmd_reboot(context, REBOOT_NORMAL); 566 | } else if (!strcmp(cmd, "bootloader")) { 567 | return fb_cmd_reboot(context, REBOOT_BOOTLOADER); 568 | } else if (!strcmp(cmd, "rcm")) { 569 | return fb_cmd_reboot(context, REBOOT_RCM); 570 | } else if (!strcmp(cmd, "recovery")) { 571 | return fb_cmd_reboot(context, REBOOT_RECOVERY); 572 | } 573 | 574 | s = simple_strtoull(cmd, &cmd, 0); 575 | if (*cmd != '\0') { 576 | return FB_BAD_COMMAND; 577 | } 578 | 579 | return fb_cmd_reboot(context, s); 580 | } 581 | 582 | static fb_status 583 | fb_cmd_oem(usbd *context, 584 | char *cmd) 585 | { 586 | fb_status status = FB_UNKNOWN_COMMAND; 587 | 588 | #define CMD_LIST \ 589 | CMD(peek) \ 590 | CMD(poke) \ 591 | CMD(echo) \ 592 | CMD(alloc) \ 593 | CMD(alloc32) \ 594 | CMD(free) \ 595 | CMD(smccc) \ 596 | CMD(reboot) \ 597 | 598 | #define CMD(x) else if (!memcmp(cmd, S(x)" ", sizeof(S(x)" ") - 1)) { \ 599 | status = fb_oem_cmd_##x(context, cmd + sizeof(S(x)" ") - 1); \ 600 | } 601 | 602 | if (0) { 603 | } CMD_LIST; 604 | 605 | #undef CMD 606 | #undef CMD_LIST 607 | 608 | return status; 609 | } 610 | 611 | static void 612 | fb_cmd_flash_complete(usbd *context, 613 | usbd_req *req) 614 | { 615 | fb_mem *fb = context->ctx; 616 | void (*binary)(void *fdt); 617 | 618 | binary = VP(fb->last_loaded); 619 | if (!memcmp(fb->last_loaded, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { 620 | /* 621 | * An Android boot image-formatted binary blob. We 622 | * completely ignore the header today. Eventually, we may 623 | * support initrd and etc. 624 | */ 625 | boot_img *img = VP(fb->last_loaded); 626 | /* 627 | * Only those binaries that can deal with the img->page_size 628 | * alignment will run. shieldTV_loader itself is okay as 629 | * it is expecting a PAGE_SIZE alignment (and img->page_size 630 | * is thus set to PAGE_SIZE, too). 631 | */ 632 | binary = VP(img->page_size + fb->last_loaded); 633 | } 634 | 635 | usbd_fini(context); 636 | binary(fb->fdt); 637 | } 638 | 639 | static fb_status 640 | fb_cmd_flash(usbd *context, 641 | char *cmd) 642 | { 643 | fb_mem *fb = context->ctx; 644 | 645 | if (strcmp(cmd, "run") != 0) { 646 | return FB_BAD_COMMAND; 647 | } 648 | 649 | if (fb->last_loaded == NULL) { 650 | return FB_NOT_DOWNLOADED; 651 | } 652 | 653 | fb_end_command_with_custom_complete(context, FB_OK, 654 | fb_cmd_flash_complete); 655 | return FB_OK; 656 | } 657 | 658 | static fb_status 659 | fb_cmd_download(usbd *context, 660 | char *cmd) 661 | { 662 | size_t size; 663 | fb_mem *fb = context->ctx; 664 | 665 | if (fb->last_loaded != NULL) { 666 | lmb_free(&lmb, 667 | (phys_addr_t) fb->last_loaded, 668 | fb->load_size, 669 | fb->load_align); 670 | fb->last_loaded = NULL; 671 | fb->load_size = 0; 672 | fb->load_align = 0; 673 | } 674 | 675 | size = simple_strtoull(cmd, &cmd, 16); 676 | if (*cmd != '\0') { 677 | return FB_BAD_COMMAND; 678 | } 679 | 680 | fb->last_loaded = VP(lmb_alloc_base(&lmb, size, 681 | DOWNLOAD_ALIGNMENT, 682 | /* Buffer will be DMA'd into by USB */ 683 | LMB_ALLOC_32BIT, 684 | LMB_BOOT, LMB_TAG("DLOD"))); 685 | if (fb->last_loaded == NULL) { 686 | return FB_OOM; 687 | } 688 | 689 | fb->load_size = size; 690 | fb->load_rem = size; 691 | fb->load_align = DOWNLOAD_ALIGNMENT; 692 | 693 | /* 694 | * Cancel pending rx_cmd, because we'll want to receive data. 695 | */ 696 | usbd_req_cancel(context, &(fb->ep1_out_req)); 697 | fb_request_data(context); 698 | fb_rx_data(context, NULL); 699 | 700 | return FB_OK; 701 | } 702 | 703 | static void 704 | fb_rx_cmd_complete(usbd *context, 705 | usbd_req *req) 706 | { 707 | fb_status status = FB_UNKNOWN_COMMAND; 708 | fb_mem *fb = context->ctx; 709 | char *cbuf = req->buffer; 710 | 711 | /* 712 | * Resubmit command. 713 | */ 714 | if (!req->cancel) { 715 | fb_rx_cmd(context, NULL); 716 | } 717 | 718 | if (req->error) { 719 | return; 720 | } 721 | 722 | if (fb->in_command) { 723 | /* 724 | * Cancel previous command. Note that this TX 725 | * request, submitted some time in the past, 726 | * is probably received by fastboot, and we 727 | * just haven't acknowledged the TX completion. 728 | * usbd_req_cancel most likely will just cancel 729 | * the completion ack. But this is fine... 730 | */ 731 | fb->in_command = false; 732 | usbd_req_cancel(context, &(fb->ep1_in_req)); 733 | } 734 | fb->in_command = true; 735 | 736 | /* 737 | * For ease of parsing, consider this to be an ASCIIZ buffer. 738 | */ 739 | cbuf[sizeof(fb->ep1_out_req.small_buffer) - 1] = '\0'; 740 | 741 | #define CMD_LIST \ 742 | CMD(oem) \ 743 | CMD_NO_PARAM(reboot) \ 744 | 745 | #define CMD(x) else \ 746 | } 747 | 748 | #define CMD_NO_PARAM(x) else if (!memcmp(cbuf, S(x), sizeof(S(x)) - 1)) { \ 749 | status = fb_cmd_##x(context,((char *) cbuf) + sizeof(S(x)) - 1); \ 750 | } 751 | 752 | if (!memcmp(cbuf, "oem ", sizeof("oem ") - 1)) { 753 | status = fb_cmd_oem(context, cbuf + sizeof("oem ") - 1); 754 | } else if (!strcmp(cbuf, "reboot")) { 755 | status = fb_cmd_reboot(context, REBOOT_NORMAL); 756 | } else if (!strcmp(cbuf, "reboot-bootloader")) { 757 | status = fb_cmd_reboot(context, REBOOT_BOOTLOADER); 758 | } else if (!memcmp(cbuf, "download:", sizeof("download:") - 1)) { 759 | status = fb_cmd_download(context, cbuf + sizeof("download:") - 1); 760 | } else if (!memcmp(cbuf, "flash:", sizeof("flash:") - 1)) { 761 | status = fb_cmd_flash(context, cbuf + sizeof("flash:") - 1); 762 | } 763 | 764 | 765 | #undef CMD 766 | #undef CMD_NO_PARAM 767 | #undef CMD_LIST 768 | 769 | if (status == FB_OK) { 770 | /* 771 | * Status already reported. 772 | */ 773 | return; 774 | } 775 | 776 | fb_end_command(context, status); 777 | 778 | } 779 | 780 | static void 781 | fb_rx_data_complete(usbd *context, 782 | usbd_req *req) 783 | { 784 | fb_mem *fb = context->ctx; 785 | 786 | if (req->error) { 787 | if (req->cancel) { 788 | /* 789 | * We're restarting everything. 790 | */ 791 | return; 792 | } 793 | } else { 794 | fb->load_rem -= req->io_done; 795 | } 796 | 797 | if (fb->load_rem == 0) { 798 | /* 799 | * Start listening for more commands again. 800 | */ 801 | fb_rx_cmd(context, NULL); 802 | fb_end_command_with_info(context, "Loaded at %p-%p", 803 | fb->last_loaded, 804 | fb->last_loaded + fb->load_size - 1); 805 | return; 806 | } 807 | 808 | /* 809 | * Submit to handle again, either to get more, or because 810 | * of a recoverable (hopefully) error. 811 | */ 812 | fb_rx_data(context, NULL); 813 | } 814 | 815 | static void 816 | fb_rx_data(struct usbd *context, usbd_req *unused) 817 | { 818 | fb_mem *fb = context->ctx; 819 | 820 | fb->ep1_out_req.buffer = fb->last_loaded + 821 | (fb->load_size - fb->load_rem); 822 | fb->ep1_out_req.buffer_length = fb->load_rem; 823 | fb->ep1_out_req.complete = fb_rx_data_complete; 824 | usbd_req_submit(context, &(fb->ep1_out_req)); 825 | } 826 | 827 | static void 828 | fb_rx_cmd(struct usbd *context, usbd_req *unused) 829 | { 830 | fb_mem *fb = context->ctx; 831 | 832 | C_ASSERT(sizeof(fb->ep1_out_req.small_buffer) >= 64); 833 | fb->ep1_out_req.buffer = fb->ep1_out_req.small_buffer; 834 | fb->ep1_out_req.buffer_length = 64; 835 | fb->ep1_out_req.complete = fb_rx_cmd_complete; 836 | usbd_req_submit(context, &(fb->ep1_out_req)); 837 | } 838 | 839 | static usbd_status 840 | fb_set_config(struct usbd *context, 841 | uint8_t config) 842 | { 843 | if (config > 1) { 844 | return USBD_CONFIG_UNSUPPORTED; 845 | } 846 | 847 | if (config == 1) { 848 | usbd_ep_enable(context, &fb_ep1_out, &fb_ep1_in); 849 | fb_rx_cmd(context, NULL); 850 | } else { 851 | fb_mem *fb = context->ctx; 852 | usbd_ep_disable(context, &fb_ep1_out, &fb_ep1_in); 853 | fb->in_command = false; 854 | usbd_req_cancel(context, &(fb->ep1_in_req)); 855 | usbd_req_cancel(context, &(fb->ep1_out_req)); 856 | } 857 | 858 | return USBD_SUCCESS; 859 | } 860 | 861 | void 862 | fb_launch(void *fdt) 863 | { 864 | fb_mem *fb; 865 | usbd_status usbd_stat; 866 | phys_addr_t usb_dma_memory; 867 | 868 | usb_dma_memory = lmb_alloc_base(&lmb, sizeof(fb_mem), 869 | USBD_TD_ALIGNMENT, 870 | LMB_ALLOC_32BIT, LMB_BOOT, 871 | LMB_TAG("UDMA")); 872 | 873 | lmb_dump_all(&lmb); 874 | 875 | fb = VP(usb_dma_memory); 876 | memset(fb, 0, sizeof(fb_mem)); 877 | fb->uctx.ehci_udc_base = TEGRA_EHCI_BASE; 878 | fb->uctx.ctx = fb; 879 | fb->uctx.eps = fb_eps; 880 | fb->uctx.fs_descs = descr_fs; 881 | fb->uctx.hs_descs = descr_hs; 882 | fb->uctx.set_config = fb_set_config; 883 | fb->fdt = fdt; 884 | 885 | usbd_req_init(&(fb->ep1_out_req), &fb_ep1_out); 886 | usbd_req_init(&(fb->ep1_in_req), &fb_ep1_in); 887 | 888 | usbd_stat = usbd_init(&(fb->uctx), fb->qtds, ELES(fb->qtds)); 889 | BUG_ON (usbd_stat != USBD_SUCCESS); 890 | 891 | while(1) { 892 | usbd_poll(&(fb->uctx)); 893 | } 894 | } 895 | -------------------------------------------------------------------------------- /fdt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libfdt - Flat Device Tree manipulation 3 | * Copyright (C) 2006 David Gibson, IBM Corporation. 4 | * 5 | * libfdt is dual licensed: you can use it either under the terms of 6 | * the GPL, or the BSD license, at your option. 7 | * 8 | * a) This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation; either version 2 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21 | * MA 02110-1301 USA 22 | * 23 | * Alternatively, 24 | * 25 | * b) Redistribution and use in source and binary forms, with or 26 | * without modification, are permitted provided that the following 27 | * conditions are met: 28 | * 29 | * 1. Redistributions of source code must retain the above 30 | * copyright notice, this list of conditions and the following 31 | * disclaimer. 32 | * 2. Redistributions in binary form must reproduce the above 33 | * copyright notice, this list of conditions and the following 34 | * disclaimer in the documentation and/or other materials 35 | * provided with the distribution. 36 | * 37 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | */ 51 | #include "libfdt_env.h" 52 | 53 | #include 54 | #include 55 | 56 | #include "libfdt_internal.h" 57 | 58 | int fdt_check_header(const void *fdt) 59 | { 60 | if (fdt_magic(fdt) == FDT_MAGIC) { 61 | /* Complete tree */ 62 | if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 63 | return -FDT_ERR_BADVERSION; 64 | if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) 65 | return -FDT_ERR_BADVERSION; 66 | } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 67 | /* Unfinished sequential-write blob */ 68 | if (fdt_size_dt_struct(fdt) == 0) 69 | return -FDT_ERR_BADSTATE; 70 | } else { 71 | return -FDT_ERR_BADMAGIC; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 78 | { 79 | const char *p; 80 | 81 | if (fdt_version(fdt) >= 0x11) 82 | if (((offset + len) < offset) 83 | || ((offset + len) > fdt_size_dt_struct(fdt))) 84 | return NULL; 85 | 86 | p = _fdt_offset_ptr(fdt, offset); 87 | 88 | if (p + len < p) 89 | return NULL; 90 | return p; 91 | } 92 | 93 | uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 94 | { 95 | const uint32_t *tagp, *lenp; 96 | uint32_t tag; 97 | int offset = startoffset; 98 | const char *p; 99 | 100 | *nextoffset = -FDT_ERR_TRUNCATED; 101 | tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 102 | if (!tagp) 103 | return FDT_END; /* premature end */ 104 | tag = fdt32_to_cpu(*tagp); 105 | offset += FDT_TAGSIZE; 106 | 107 | *nextoffset = -FDT_ERR_BADSTRUCTURE; 108 | switch (tag) { 109 | case FDT_BEGIN_NODE: 110 | /* skip name */ 111 | do { 112 | p = fdt_offset_ptr(fdt, offset++, 1); 113 | } while (p && (*p != '\0')); 114 | if (!p) 115 | return FDT_END; /* premature end */ 116 | break; 117 | 118 | case FDT_PROP: 119 | lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 120 | if (!lenp) 121 | return FDT_END; /* premature end */ 122 | /* skip-name offset, length and value */ 123 | offset += sizeof(struct fdt_property) - FDT_TAGSIZE 124 | + fdt32_to_cpu(*lenp); 125 | break; 126 | 127 | case FDT_END: 128 | case FDT_END_NODE: 129 | case FDT_NOP: 130 | break; 131 | 132 | default: 133 | return FDT_END; 134 | } 135 | 136 | if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 137 | return FDT_END; /* premature end */ 138 | 139 | *nextoffset = FDT_TAGALIGN(offset); 140 | return tag; 141 | } 142 | 143 | int _fdt_check_node_offset(const void *fdt, int offset) 144 | { 145 | if ((offset < 0) || (offset % FDT_TAGSIZE) 146 | || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 147 | return -FDT_ERR_BADOFFSET; 148 | 149 | return offset; 150 | } 151 | 152 | int fdt_next_node(const void *fdt, int offset, int *depth) 153 | { 154 | int nextoffset = 0; 155 | uint32_t tag; 156 | 157 | if (offset >= 0) 158 | if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) 159 | return nextoffset; 160 | 161 | do { 162 | offset = nextoffset; 163 | tag = fdt_next_tag(fdt, offset, &nextoffset); 164 | 165 | switch (tag) { 166 | case FDT_PROP: 167 | case FDT_NOP: 168 | break; 169 | 170 | case FDT_BEGIN_NODE: 171 | if (depth) 172 | (*depth)++; 173 | break; 174 | 175 | case FDT_END_NODE: 176 | if (depth && ((--(*depth)) < 0)) 177 | return nextoffset; 178 | break; 179 | 180 | case FDT_END: 181 | if ((nextoffset >= 0) 182 | || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 183 | return -FDT_ERR_NOTFOUND; 184 | else 185 | return nextoffset; 186 | } 187 | } while (tag != FDT_BEGIN_NODE); 188 | 189 | return offset; 190 | } 191 | 192 | const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) 193 | { 194 | int len = strlen(s) + 1; 195 | const char *last = strtab + tabsize - len; 196 | const char *p; 197 | 198 | for (p = strtab; p <= last; p++) 199 | if (memcmp(p, s, len) == 0) 200 | return p; 201 | return NULL; 202 | } 203 | 204 | int fdt_move(const void *fdt, void *buf, int bufsize) 205 | { 206 | FDT_CHECK_HEADER(fdt); 207 | 208 | if (fdt_totalsize(fdt) > bufsize) 209 | return -FDT_ERR_NOSPACE; 210 | 211 | memmove(buf, fdt, fdt_totalsize(fdt)); 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /fdt.h: -------------------------------------------------------------------------------- 1 | #ifndef _FDT_H 2 | #define _FDT_H 3 | 4 | #ifndef __ASSEMBLY__ 5 | 6 | struct fdt_header { 7 | uint32_t magic; /* magic word FDT_MAGIC */ 8 | uint32_t totalsize; /* total size of DT block */ 9 | uint32_t off_dt_struct; /* offset to structure */ 10 | uint32_t off_dt_strings; /* offset to strings */ 11 | uint32_t off_mem_rsvmap; /* offset to memory reserve map */ 12 | uint32_t version; /* format version */ 13 | uint32_t last_comp_version; /* last compatible version */ 14 | 15 | /* version 2 fields below */ 16 | uint32_t boot_cpuid_phys; /* Which physical CPU id we're 17 | booting on */ 18 | /* version 3 fields below */ 19 | uint32_t size_dt_strings; /* size of the strings block */ 20 | 21 | /* version 17 fields below */ 22 | uint32_t size_dt_struct; /* size of the structure block */ 23 | }; 24 | 25 | struct fdt_reserve_entry { 26 | uint64_t address; 27 | uint64_t size; 28 | }; 29 | 30 | struct fdt_node_header { 31 | uint32_t tag; 32 | char name[0]; 33 | }; 34 | 35 | struct fdt_property { 36 | uint32_t tag; 37 | uint32_t len; 38 | uint32_t nameoff; 39 | char data[0]; 40 | }; 41 | 42 | #endif /* !__ASSEMBLY */ 43 | 44 | #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ 45 | #define FDT_TAGSIZE sizeof(uint32_t) 46 | 47 | #define FDT_BEGIN_NODE 0x1 /* Start node: full name */ 48 | #define FDT_END_NODE 0x2 /* End node */ 49 | #define FDT_PROP 0x3 /* Property: name off, 50 | size, content */ 51 | #define FDT_NOP 0x4 /* nop */ 52 | #define FDT_END 0x9 53 | 54 | #define FDT_V1_SIZE (7*sizeof(uint32_t)) 55 | #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) 56 | #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) 57 | #define FDT_V16_SIZE FDT_V3_SIZE 58 | #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) 59 | 60 | #endif /* _FDT_H */ 61 | -------------------------------------------------------------------------------- /fdt_ro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libfdt - Flat Device Tree manipulation 3 | * Copyright (C) 2006 David Gibson, IBM Corporation. 4 | * 5 | * libfdt is dual licensed: you can use it either under the terms of 6 | * the GPL, or the BSD license, at your option. 7 | * 8 | * a) This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation; either version 2 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21 | * MA 02110-1301 USA 22 | * 23 | * Alternatively, 24 | * 25 | * b) Redistribution and use in source and binary forms, with or 26 | * without modification, are permitted provided that the following 27 | * conditions are met: 28 | * 29 | * 1. Redistributions of source code must retain the above 30 | * copyright notice, this list of conditions and the following 31 | * disclaimer. 32 | * 2. Redistributions in binary form must reproduce the above 33 | * copyright notice, this list of conditions and the following 34 | * disclaimer in the documentation and/or other materials 35 | * provided with the distribution. 36 | * 37 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | */ 51 | #include "libfdt_env.h" 52 | 53 | #include 54 | #include 55 | 56 | #include "libfdt_internal.h" 57 | 58 | static int _fdt_nodename_eq(const void *fdt, int offset, 59 | const char *s, int len) 60 | { 61 | const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 62 | 63 | if (! p) 64 | /* short match */ 65 | return 0; 66 | 67 | if (memcmp(p, s, len) != 0) 68 | return 0; 69 | 70 | if (p[len] == '\0') 71 | return 1; 72 | else if (!memchr(s, '@', len) && (p[len] == '@')) 73 | return 1; 74 | else 75 | return 0; 76 | } 77 | 78 | const char *fdt_string(const void *fdt, int stroffset) 79 | { 80 | return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 81 | } 82 | 83 | static int _fdt_string_eq(const void *fdt, int stroffset, 84 | const char *s, int len) 85 | { 86 | const char *p = fdt_string(fdt, stroffset); 87 | 88 | return (strlen(p) == len) && (memcmp(p, s, len) == 0); 89 | } 90 | 91 | int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 92 | { 93 | FDT_CHECK_HEADER(fdt); 94 | *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 95 | *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 96 | return 0; 97 | } 98 | 99 | int fdt_num_mem_rsv(const void *fdt) 100 | { 101 | int i = 0; 102 | 103 | while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 104 | i++; 105 | return i; 106 | } 107 | 108 | int fdt_subnode_offset_namelen(const void *fdt, int offset, 109 | const char *name, int namelen) 110 | { 111 | int depth; 112 | 113 | FDT_CHECK_HEADER(fdt); 114 | 115 | for (depth = 0; 116 | (offset >= 0) && (depth >= 0); 117 | offset = fdt_next_node(fdt, offset, &depth)) 118 | if ((depth == 1) 119 | && _fdt_nodename_eq(fdt, offset, name, namelen)) 120 | return offset; 121 | 122 | if (depth < 0) 123 | return -FDT_ERR_NOTFOUND; 124 | return offset; /* error */ 125 | } 126 | 127 | int fdt_sibling_offset_namelen(const void *fdt, int offset, 128 | const char *name, int namelen) 129 | { 130 | int depth; 131 | 132 | FDT_CHECK_HEADER(fdt); 133 | 134 | for (depth = 1, offset = fdt_next_node(fdt, offset, &depth); 135 | (offset >= 0) && (depth >= 1); 136 | offset = fdt_next_node(fdt, offset, &depth)) { 137 | if ((depth == 1) 138 | && _fdt_nodename_eq(fdt, offset, name, namelen)) 139 | return offset; 140 | } 141 | 142 | if (depth < 0) 143 | return -FDT_ERR_NOTFOUND; 144 | return offset; /* error */ 145 | } 146 | 147 | int fdt_subnode_offset(const void *fdt, int parentoffset, 148 | const char *name) 149 | { 150 | return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 151 | } 152 | 153 | int fdt_sibling_offset(const void *fdt, int offset, 154 | const char *name) 155 | { 156 | return fdt_sibling_offset_namelen(fdt, offset, name, strlen(name)); 157 | } 158 | 159 | int fdt_path_offset(const void *fdt, const char *path) 160 | { 161 | const char *end = path + strlen(path); 162 | const char *p = path; 163 | int offset = 0; 164 | 165 | FDT_CHECK_HEADER(fdt); 166 | 167 | /* see if we have an alias */ 168 | if (*path != '/') { 169 | const char *q = strchr(path, '/'); 170 | 171 | if (!q) 172 | q = end; 173 | 174 | p = fdt_get_alias_namelen(fdt, p, q - p); 175 | if (!p) 176 | return -FDT_ERR_BADPATH; 177 | offset = fdt_path_offset(fdt, p); 178 | 179 | p = q; 180 | } 181 | 182 | while (*p) { 183 | const char *q; 184 | 185 | while (*p == '/') 186 | p++; 187 | if (! *p) 188 | return offset; 189 | q = strchr(p, '/'); 190 | if (! q) 191 | q = end; 192 | 193 | offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 194 | if (offset < 0) 195 | return offset; 196 | 197 | p = q; 198 | } 199 | 200 | return offset; 201 | } 202 | 203 | const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 204 | { 205 | const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 206 | int err; 207 | 208 | if (((err = fdt_check_header(fdt)) != 0) 209 | || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 210 | goto fail; 211 | 212 | if (len) 213 | *len = strlen(nh->name); 214 | 215 | return nh->name; 216 | 217 | fail: 218 | if (len) 219 | *len = err; 220 | return NULL; 221 | } 222 | 223 | const struct fdt_property *fdt_get_property_namelen(const void *fdt, 224 | int nodeoffset, 225 | const char *name, 226 | int namelen, int *lenp) 227 | { 228 | uint32_t tag; 229 | const struct fdt_property *prop; 230 | int offset, nextoffset; 231 | int err; 232 | 233 | if (((err = fdt_check_header(fdt)) != 0) 234 | || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 235 | goto fail; 236 | 237 | nextoffset = err; 238 | do { 239 | offset = nextoffset; 240 | 241 | tag = fdt_next_tag(fdt, offset, &nextoffset); 242 | switch (tag) { 243 | case FDT_END: 244 | if (nextoffset < 0) 245 | err = nextoffset; 246 | else 247 | /* FDT_END tag with unclosed nodes */ 248 | err = -FDT_ERR_BADSTRUCTURE; 249 | goto fail; 250 | 251 | case FDT_PROP: 252 | prop = _fdt_offset_ptr(fdt, offset); 253 | if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 254 | name, namelen)) { 255 | /* Found it! */ 256 | if (lenp) 257 | *lenp = fdt32_to_cpu(prop->len); 258 | 259 | return prop; 260 | } 261 | break; 262 | } 263 | } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); 264 | 265 | err = -FDT_ERR_NOTFOUND; 266 | fail: 267 | if (lenp) 268 | *lenp = err; 269 | return NULL; 270 | } 271 | 272 | const struct fdt_property *fdt_get_property(const void *fdt, 273 | int nodeoffset, 274 | const char *name, int *lenp) 275 | { 276 | return fdt_get_property_namelen(fdt, nodeoffset, name, 277 | strlen(name), lenp); 278 | } 279 | 280 | const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 281 | const char *name, int namelen, int *lenp) 282 | { 283 | const struct fdt_property *prop; 284 | 285 | prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 286 | if (! prop) 287 | return NULL; 288 | 289 | return prop->data; 290 | } 291 | 292 | const void *fdt_getprop(const void *fdt, int nodeoffset, 293 | const char *name, int *lenp) 294 | { 295 | return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 296 | } 297 | 298 | uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 299 | { 300 | const uint32_t *php; 301 | int len; 302 | 303 | php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 304 | if (!php || (len != sizeof(*php))) 305 | return 0; 306 | 307 | return fdt32_to_cpu(*php); 308 | } 309 | 310 | const char *fdt_get_alias_namelen(const void *fdt, 311 | const char *name, int namelen) 312 | { 313 | int aliasoffset; 314 | 315 | aliasoffset = fdt_path_offset(fdt, "/aliases"); 316 | if (aliasoffset < 0) 317 | return NULL; 318 | 319 | return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 320 | } 321 | 322 | const char *fdt_get_alias(const void *fdt, const char *name) 323 | { 324 | return fdt_get_alias_namelen(fdt, name, strlen(name)); 325 | } 326 | 327 | int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 328 | { 329 | int pdepth = 0, p = 0; 330 | int offset, depth, namelen; 331 | const char *name; 332 | 333 | FDT_CHECK_HEADER(fdt); 334 | 335 | if (buflen < 2) 336 | return -FDT_ERR_NOSPACE; 337 | 338 | for (offset = 0, depth = 0; 339 | (offset >= 0) && (offset <= nodeoffset); 340 | offset = fdt_next_node(fdt, offset, &depth)) { 341 | while (pdepth > depth) { 342 | do { 343 | p--; 344 | } while (buf[p-1] != '/'); 345 | pdepth--; 346 | } 347 | 348 | if (pdepth >= depth) { 349 | name = fdt_get_name(fdt, offset, &namelen); 350 | if (!name) 351 | return namelen; 352 | if ((p + namelen + 1) <= buflen) { 353 | memcpy(buf + p, name, namelen); 354 | p += namelen; 355 | buf[p++] = '/'; 356 | pdepth++; 357 | } 358 | } 359 | 360 | if (offset == nodeoffset) { 361 | if (pdepth < (depth + 1)) 362 | return -FDT_ERR_NOSPACE; 363 | 364 | if (p > 1) /* special case so that root path is "/", not "" */ 365 | p--; 366 | buf[p] = '\0'; 367 | return 0; 368 | } 369 | } 370 | 371 | if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 372 | return -FDT_ERR_BADOFFSET; 373 | else if (offset == -FDT_ERR_BADOFFSET) 374 | return -FDT_ERR_BADSTRUCTURE; 375 | 376 | return offset; /* error from fdt_next_node() */ 377 | } 378 | 379 | int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 380 | int supernodedepth, int *nodedepth) 381 | { 382 | int offset, depth; 383 | int supernodeoffset = -FDT_ERR_INTERNAL; 384 | 385 | FDT_CHECK_HEADER(fdt); 386 | 387 | if (supernodedepth < 0) 388 | return -FDT_ERR_NOTFOUND; 389 | 390 | for (offset = 0, depth = 0; 391 | (offset >= 0) && (offset <= nodeoffset); 392 | offset = fdt_next_node(fdt, offset, &depth)) { 393 | if (depth == supernodedepth) 394 | supernodeoffset = offset; 395 | 396 | if (offset == nodeoffset) { 397 | if (nodedepth) 398 | *nodedepth = depth; 399 | 400 | if (supernodedepth > depth) 401 | return -FDT_ERR_NOTFOUND; 402 | else 403 | return supernodeoffset; 404 | } 405 | } 406 | 407 | if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 408 | return -FDT_ERR_BADOFFSET; 409 | else if (offset == -FDT_ERR_BADOFFSET) 410 | return -FDT_ERR_BADSTRUCTURE; 411 | 412 | return offset; /* error from fdt_next_node() */ 413 | } 414 | 415 | int fdt_node_depth(const void *fdt, int nodeoffset) 416 | { 417 | int nodedepth; 418 | int err; 419 | 420 | err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 421 | if (err) 422 | return (err < 0) ? err : -FDT_ERR_INTERNAL; 423 | return nodedepth; 424 | } 425 | 426 | int fdt_parent_offset(const void *fdt, int nodeoffset) 427 | { 428 | int nodedepth = fdt_node_depth(fdt, nodeoffset); 429 | 430 | if (nodedepth < 0) 431 | return nodedepth; 432 | return fdt_supernode_atdepth_offset(fdt, nodeoffset, 433 | nodedepth - 1, NULL); 434 | } 435 | 436 | int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 437 | const char *propname, 438 | const void *propval, int proplen) 439 | { 440 | int offset; 441 | const void *val; 442 | int len; 443 | 444 | FDT_CHECK_HEADER(fdt); 445 | 446 | /* FIXME: The algorithm here is pretty horrible: we scan each 447 | * property of a node in fdt_getprop(), then if that didn't 448 | * find what we want, we scan over them again making our way 449 | * to the next node. Still it's the easiest to implement 450 | * approach; performance can come later. */ 451 | for (offset = fdt_next_node(fdt, startoffset, NULL); 452 | offset >= 0; 453 | offset = fdt_next_node(fdt, offset, NULL)) { 454 | val = fdt_getprop(fdt, offset, propname, &len); 455 | if (val && (len == proplen) 456 | && (memcmp(val, propval, len) == 0)) 457 | return offset; 458 | } 459 | 460 | return offset; /* error from fdt_next_node() */ 461 | } 462 | 463 | int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 464 | { 465 | if ((phandle == 0) || (phandle == -1)) 466 | return -FDT_ERR_BADPHANDLE; 467 | phandle = cpu_to_fdt32(phandle); 468 | return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle", 469 | &phandle, sizeof(phandle)); 470 | } 471 | 472 | static int _fdt_stringlist_contains(const char *strlist, int listlen, 473 | const char *str) 474 | { 475 | int len = strlen(str); 476 | const char *p; 477 | 478 | while (listlen >= len) { 479 | if (memcmp(str, strlist, len+1) == 0) 480 | return 1; 481 | p = memchr(strlist, '\0', listlen); 482 | if (!p) 483 | return 0; /* malformed strlist.. */ 484 | listlen -= (p-strlist) + 1; 485 | strlist = p + 1; 486 | } 487 | return 0; 488 | } 489 | 490 | int fdt_node_check_compatible(const void *fdt, int nodeoffset, 491 | const char *compatible) 492 | { 493 | const void *prop; 494 | int len; 495 | 496 | prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 497 | if (!prop) 498 | return len; 499 | if (_fdt_stringlist_contains(prop, len, compatible)) 500 | return 0; 501 | else 502 | return 1; 503 | } 504 | 505 | int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 506 | const char *compatible) 507 | { 508 | int offset, err; 509 | 510 | FDT_CHECK_HEADER(fdt); 511 | 512 | /* FIXME: The algorithm here is pretty horrible: we scan each 513 | * property of a node in fdt_node_check_compatible(), then if 514 | * that didn't find what we want, we scan over them again 515 | * making our way to the next node. Still it's the easiest to 516 | * implement approach; performance can come later. */ 517 | for (offset = fdt_next_node(fdt, startoffset, NULL); 518 | offset >= 0; 519 | offset = fdt_next_node(fdt, offset, NULL)) { 520 | err = fdt_node_check_compatible(fdt, offset, compatible); 521 | if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 522 | return err; 523 | else if (err == 0) 524 | return offset; 525 | } 526 | 527 | return offset; /* error from fdt_next_node() */ 528 | } 529 | 530 | int fdt_node_check_dtype(const void *fdt, int nodeoffset, 531 | const char *dtype) 532 | { 533 | const void *prop; 534 | int len; 535 | 536 | prop = fdt_getprop(fdt, nodeoffset, "device_type", &len); 537 | if (!prop) 538 | return len; 539 | if (_fdt_stringlist_contains(prop, len, dtype)) 540 | return 0; 541 | else 542 | return 1; 543 | } 544 | 545 | int fdt_node_offset_by_dtype(const void *fdt, int startoffset, 546 | const char *dtype) 547 | { 548 | int offset, err; 549 | 550 | FDT_CHECK_HEADER(fdt); 551 | 552 | /* FIXME: The algorithm here is pretty horrible: we scan each 553 | * property of a node in fdt_node_check_dtype(), then if 554 | * that didn't find what we want, we scan over them again 555 | * making our way to the next node. Still it's the easiest to 556 | * implement approach; performance can come later. */ 557 | for (offset = fdt_next_node(fdt, startoffset, NULL); 558 | offset >= 0; 559 | offset = fdt_next_node(fdt, offset, NULL)) { 560 | err = fdt_node_check_dtype(fdt, offset, dtype); 561 | if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 562 | return err; 563 | else if (err == 0) 564 | return offset; 565 | } 566 | 567 | return offset; /* error from fdt_next_node() */ 568 | } 569 | -------------------------------------------------------------------------------- /fdt_strerror.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libfdt - Flat Device Tree manipulation 3 | * Copyright (C) 2006 David Gibson, IBM Corporation. 4 | * 5 | * libfdt is dual licensed: you can use it either under the terms of 6 | * the GPL, or the BSD license, at your option. 7 | * 8 | * a) This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation; either version 2 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21 | * MA 02110-1301 USA 22 | * 23 | * Alternatively, 24 | * 25 | * b) Redistribution and use in source and binary forms, with or 26 | * without modification, are permitted provided that the following 27 | * conditions are met: 28 | * 29 | * 1. Redistributions of source code must retain the above 30 | * copyright notice, this list of conditions and the following 31 | * disclaimer. 32 | * 2. Redistributions in binary form must reproduce the above 33 | * copyright notice, this list of conditions and the following 34 | * disclaimer in the documentation and/or other materials 35 | * provided with the distribution. 36 | * 37 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | */ 51 | #include 52 | #include "libfdt_env.h" 53 | #include 54 | #include 55 | 56 | #include "libfdt_internal.h" 57 | 58 | struct fdt_errtabent { 59 | const char *str; 60 | }; 61 | 62 | #define FDT_ERRTABENT(val) \ 63 | [(val)] = { .str = #val, } 64 | 65 | static struct fdt_errtabent fdt_errtable[] = { 66 | FDT_ERRTABENT(FDT_ERR_NOTFOUND), 67 | FDT_ERRTABENT(FDT_ERR_EXISTS), 68 | FDT_ERRTABENT(FDT_ERR_NOSPACE), 69 | 70 | FDT_ERRTABENT(FDT_ERR_BADOFFSET), 71 | FDT_ERRTABENT(FDT_ERR_BADPATH), 72 | FDT_ERRTABENT(FDT_ERR_BADSTATE), 73 | 74 | FDT_ERRTABENT(FDT_ERR_TRUNCATED), 75 | FDT_ERRTABENT(FDT_ERR_BADMAGIC), 76 | FDT_ERRTABENT(FDT_ERR_BADVERSION), 77 | FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), 78 | FDT_ERRTABENT(FDT_ERR_BADLAYOUT), 79 | }; 80 | #define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) 81 | 82 | const char * __attrconst fdt_strerror(int errval) 83 | { 84 | if (errval > 0) 85 | return ""; 86 | else if (errval == 0) 87 | return ""; 88 | else if (errval > -FDT_ERRTABSIZE) { 89 | const char *s = fdt_errtable[-errval].str; 90 | 91 | if (s) 92 | return s; 93 | } 94 | 95 | return ""; 96 | } 97 | -------------------------------------------------------------------------------- /lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | void 25 | printk(char *fmt, ...) 26 | { 27 | va_list list; 28 | char buf[512]; 29 | va_start(list, fmt); 30 | vsnprintf(buf, sizeof(buf), fmt, list); 31 | video_puts(buf); 32 | va_end(list); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | #ifndef LIB_H 21 | #define LIB_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | void printk(char *fmt, ...); 30 | 31 | #define BUG() do { \ 32 | printk("%s:%u BUG ()\n", __FILE__, __LINE__); \ 33 | while (1); \ 34 | } while(0); 35 | 36 | #define BUG_ON(condition) do { \ 37 | if (unlikely(condition)) { \ 38 | printk("%s:%u BUG (%s)\n", __FILE__, __LINE__, \ 39 | S(condition)); \ 40 | while (1); \ 41 | } \ 42 | } while(0) 43 | 44 | #define BUG_ON_EX(condition, fmt, ...) do { \ 45 | if (unlikely(condition)) { \ 46 | printk("%s:%u BUG (%s): " fmt "\n", __FILE__, __LINE__, \ 47 | S(condition), ## __VA_ARGS__); \ 48 | while (1); \ 49 | } \ 50 | } while(0) 51 | 52 | #endif /* LIB_H */ 53 | -------------------------------------------------------------------------------- /libfdt.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBFDT_H 2 | #define _LIBFDT_H 3 | /* 4 | * libfdt - Flat Device Tree manipulation 5 | * Copyright (C) 2006 David Gibson, IBM Corporation. 6 | * 7 | * libfdt is dual licensed: you can use it either under the terms of 8 | * the GPL, or the BSD license, at your option. 9 | * 10 | * a) This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU General Public License as 12 | * published by the Free Software Foundation; either version 2 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public 21 | * License along with this library; if not, write to the Free 22 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 23 | * MA 02110-1301 USA 24 | * 25 | * Alternatively, 26 | * 27 | * b) Redistribution and use in source and binary forms, with or 28 | * without modification, are permitted provided that the following 29 | * conditions are met: 30 | * 31 | * 1. Redistributions of source code must retain the above 32 | * copyright notice, this list of conditions and the following 33 | * disclaimer. 34 | * 2. Redistributions in binary form must reproduce the above 35 | * copyright notice, this list of conditions and the following 36 | * disclaimer in the documentation and/or other materials 37 | * provided with the distribution. 38 | * 39 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 40 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 41 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 42 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 44 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 49 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 50 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 51 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | */ 53 | 54 | #include 55 | #include 56 | 57 | #define FDT_FIRST_SUPPORTED_VERSION 0x10 58 | #define FDT_LAST_SUPPORTED_VERSION 0x11 59 | 60 | /* Error codes: informative error codes */ 61 | #define FDT_ERR_NOTFOUND 1 62 | /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ 63 | #define FDT_ERR_EXISTS 2 64 | /* FDT_ERR_EXISTS: Attemped to create a node or property which 65 | * already exists */ 66 | #define FDT_ERR_NOSPACE 3 67 | /* FDT_ERR_NOSPACE: Operation needed to expand the device 68 | * tree, but its buffer did not have sufficient space to 69 | * contain the expanded tree. Use fdt_open_into() to move the 70 | * device tree to a buffer with more space. */ 71 | 72 | /* Error codes: codes for bad parameters */ 73 | #define FDT_ERR_BADOFFSET 4 74 | /* FDT_ERR_BADOFFSET: Function was passed a structure block 75 | * offset which is out-of-bounds, or which points to an 76 | * unsuitable part of the structure for the operation. */ 77 | #define FDT_ERR_BADPATH 5 78 | /* FDT_ERR_BADPATH: Function was passed a badly formatted path 79 | * (e.g. missing a leading / for a function which requires an 80 | * absolute path) */ 81 | #define FDT_ERR_BADPHANDLE 6 82 | /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle 83 | * value. phandle values of 0 and -1 are not permitted. */ 84 | #define FDT_ERR_BADSTATE 7 85 | /* FDT_ERR_BADSTATE: Function was passed an incomplete device 86 | * tree created by the sequential-write functions, which is 87 | * not sufficiently complete for the requested operation. */ 88 | 89 | /* Error codes: codes for bad device tree blobs */ 90 | #define FDT_ERR_TRUNCATED 8 91 | /* FDT_ERR_TRUNCATED: Structure block of the given device tree 92 | * ends without an FDT_END tag. */ 93 | #define FDT_ERR_BADMAGIC 9 94 | /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a 95 | * device tree at all - it is missing the flattened device 96 | * tree magic number. */ 97 | #define FDT_ERR_BADVERSION 10 98 | /* FDT_ERR_BADVERSION: Given device tree has a version which 99 | * can't be handled by the requested operation. For 100 | * read-write functions, this may mean that fdt_open_into() is 101 | * required to convert the tree to the expected version. */ 102 | #define FDT_ERR_BADSTRUCTURE 11 103 | /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt 104 | * structure block or other serious error (e.g. misnested 105 | * nodes, or subnodes preceding properties). */ 106 | #define FDT_ERR_BADLAYOUT 12 107 | /* FDT_ERR_BADLAYOUT: For read-write functions, the given 108 | * device tree has it's sub-blocks in an order that the 109 | * function can't handle (memory reserve map, then structure, 110 | * then strings). Use fdt_open_into() to reorganize the tree 111 | * into a form suitable for the read-write operations. */ 112 | 113 | /* "Can't happen" error indicating a bug in libfdt */ 114 | #define FDT_ERR_INTERNAL 13 115 | /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. 116 | * Should never be returned, if it is, it indicates a bug in 117 | * libfdt itself. */ 118 | 119 | #define FDT_ERR_MAX 13 120 | 121 | /**********************************************************************/ 122 | /* Low-level functions (you probably don't need these) */ 123 | /**********************************************************************/ 124 | 125 | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); 126 | static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) 127 | { 128 | return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); 129 | } 130 | 131 | uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); 132 | 133 | /**********************************************************************/ 134 | /* Traversal functions */ 135 | /**********************************************************************/ 136 | 137 | int fdt_next_node(const void *fdt, int offset, int *depth); 138 | 139 | /**********************************************************************/ 140 | /* General functions */ 141 | /**********************************************************************/ 142 | 143 | #define fdt_get_header(fdt, field) \ 144 | (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) 145 | #define fdt_magic(fdt) (fdt_get_header(fdt, magic)) 146 | #define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) 147 | #define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) 148 | #define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) 149 | #define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) 150 | #define fdt_version(fdt) (fdt_get_header(fdt, version)) 151 | #define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) 152 | #define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) 153 | #define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) 154 | #define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) 155 | 156 | #define __fdt_set_hdr(name) \ 157 | static inline void fdt_set_##name(void *fdt, uint32_t val) \ 158 | { \ 159 | struct fdt_header *fdth = (struct fdt_header*)fdt; \ 160 | fdth->name = cpu_to_fdt32(val); \ 161 | } 162 | __fdt_set_hdr(magic); 163 | __fdt_set_hdr(totalsize); 164 | __fdt_set_hdr(off_dt_struct); 165 | __fdt_set_hdr(off_dt_strings); 166 | __fdt_set_hdr(off_mem_rsvmap); 167 | __fdt_set_hdr(version); 168 | __fdt_set_hdr(last_comp_version); 169 | __fdt_set_hdr(boot_cpuid_phys); 170 | __fdt_set_hdr(size_dt_strings); 171 | __fdt_set_hdr(size_dt_struct); 172 | #undef __fdt_set_hdr 173 | 174 | /** 175 | * fdt_check_header - sanity check a device tree or possible device tree 176 | * @fdt: pointer to data which might be a flattened device tree 177 | * 178 | * fdt_check_header() checks that the given buffer contains what 179 | * appears to be a flattened device tree with sane information in its 180 | * header. 181 | * 182 | * returns: 183 | * 0, if the buffer appears to contain a valid device tree 184 | * -FDT_ERR_BADMAGIC, 185 | * -FDT_ERR_BADVERSION, 186 | * -FDT_ERR_BADSTATE, standard meanings, as above 187 | */ 188 | int fdt_check_header(const void *fdt); 189 | 190 | /** 191 | * fdt_move - move a device tree around in memory 192 | * @fdt: pointer to the device tree to move 193 | * @buf: pointer to memory where the device is to be moved 194 | * @bufsize: size of the memory space at buf 195 | * 196 | * fdt_move() relocates, if possible, the device tree blob located at 197 | * fdt to the buffer at buf of size bufsize. The buffer may overlap 198 | * with the existing device tree blob at fdt. Therefore, 199 | * fdt_move(fdt, fdt, fdt_totalsize(fdt)) 200 | * should always succeed. 201 | * 202 | * returns: 203 | * 0, on success 204 | * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree 205 | * -FDT_ERR_BADMAGIC, 206 | * -FDT_ERR_BADVERSION, 207 | * -FDT_ERR_BADSTATE, standard meanings 208 | */ 209 | int fdt_move(const void *fdt, void *buf, int bufsize); 210 | 211 | /**********************************************************************/ 212 | /* Read-only functions */ 213 | /**********************************************************************/ 214 | 215 | /** 216 | * fdt_string - retrieve a string from the strings block of a device tree 217 | * @fdt: pointer to the device tree blob 218 | * @stroffset: offset of the string within the strings block (native endian) 219 | * 220 | * fdt_string() retrieves a pointer to a single string from the 221 | * strings block of the device tree blob at fdt. 222 | * 223 | * returns: 224 | * a pointer to the string, on success 225 | * NULL, if stroffset is out of bounds 226 | */ 227 | const char *fdt_string(const void *fdt, int stroffset); 228 | 229 | /** 230 | * fdt_num_mem_rsv - retrieve the number of memory reserve map entries 231 | * @fdt: pointer to the device tree blob 232 | * 233 | * Returns the number of entries in the device tree blob's memory 234 | * reservation map. This does not include the terminating 0,0 entry 235 | * or any other (0,0) entries reserved for expansion. 236 | * 237 | * returns: 238 | * the number of entries 239 | */ 240 | int fdt_num_mem_rsv(const void *fdt); 241 | 242 | /** 243 | * fdt_get_mem_rsv - retrieve one memory reserve map entry 244 | * @fdt: pointer to the device tree blob 245 | * @address, @size: pointers to 64-bit variables 246 | * 247 | * On success, *address and *size will contain the address and size of 248 | * the n-th reserve map entry from the device tree blob, in 249 | * native-endian format. 250 | * 251 | * returns: 252 | * 0, on success 253 | * -FDT_ERR_BADMAGIC, 254 | * -FDT_ERR_BADVERSION, 255 | * -FDT_ERR_BADSTATE, standard meanings 256 | */ 257 | int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); 258 | 259 | /** 260 | * fdt_subnode_offset_namelen - find a subnode based on substring 261 | * @fdt: pointer to the device tree blob 262 | * @parentoffset: structure block offset of a node 263 | * @name: name of the subnode to locate 264 | * @namelen: number of characters of name to consider 265 | * 266 | * Identical to fdt_subnode_offset(), but only examine the first 267 | * namelen characters of name for matching the subnode name. This is 268 | * useful for finding subnodes based on a portion of a larger string, 269 | * such as a full path. 270 | */ 271 | int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, 272 | const char *name, int namelen); 273 | /** 274 | * fdt_sibling_offset_namelen - find sibling node based on substring 275 | * @fdt: pointer to the device tree blob 276 | * @fromoffset: node to start from 277 | * @name: name of the subnode to locate 278 | * @namelen: number of characters of name to consider 279 | * 280 | * Typically used to continue the search started with 281 | * fdt_subnode_offset_namelen() using the same matching rules. 282 | */ 283 | int fdt_sibling_offset_namelen(const void *fdt, int fromoffset, 284 | const char *name, int namelen); 285 | /** 286 | * fdt_subnode_offset - find a subnode of a given node 287 | * @fdt: pointer to the device tree blob 288 | * @parentoffset: structure block offset of a node 289 | * @name: name of the subnode to locate 290 | * 291 | * fdt_subnode_offset() finds a subnode of the node at structure block 292 | * offset parentoffset with the given name. name may include a unit 293 | * address, in which case fdt_subnode_offset() will find the subnode 294 | * with that unit address, or the unit address may be omitted, in 295 | * which case fdt_subnode_offset() will find an arbitrary subnode 296 | * whose name excluding unit address matches the given name. 297 | * 298 | * returns: 299 | * structure block offset of the requested subnode (>=0), on success 300 | * -FDT_ERR_NOTFOUND, if the requested subnode does not exist 301 | * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag 302 | * -FDT_ERR_BADMAGIC, 303 | * -FDT_ERR_BADVERSION, 304 | * -FDT_ERR_BADSTATE, 305 | * -FDT_ERR_BADSTRUCTURE, 306 | * -FDT_ERR_TRUNCATED, standard meanings. 307 | */ 308 | int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); 309 | 310 | /** 311 | * fdt_sibling_offset - find a sibling of a given node by name 312 | * @fdt: pointer to the device tree blob 313 | * @fromoffset: structure block offset of a node 314 | * @name: name of the subnode to locate 315 | * 316 | * Typically used to continue the search started with fdt_subnode_offset() 317 | * using the same matching rules. 318 | * 319 | * returns: 320 | * structure block offset of the requested subnode (>=0), on success 321 | * -FDT_ERR_NOTFOUND, if the requested subnode does not exist 322 | * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag 323 | * -FDT_ERR_BADMAGIC, 324 | * -FDT_ERR_BADVERSION, 325 | * -FDT_ERR_BADSTATE, 326 | * -FDT_ERR_BADSTRUCTURE, 327 | * -FDT_ERR_TRUNCATED, standard meanings. 328 | */ 329 | int fdt_sibling_offset(const void *fdt, int fromoffset, const char *name); 330 | 331 | /** 332 | * fdt_path_offset - find a tree node by its full path 333 | * @fdt: pointer to the device tree blob 334 | * @path: full path of the node to locate 335 | * 336 | * fdt_path_offset() finds a node of a given path in the device tree. 337 | * Each path component may omit the unit address portion, but the 338 | * results of this are undefined if any such path component is 339 | * ambiguous (that is if there are multiple nodes at the relevant 340 | * level matching the given component, differentiated only by unit 341 | * address). 342 | * 343 | * returns: 344 | * structure block offset of the node with the requested path (>=0), on success 345 | * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid 346 | * -FDT_ERR_NOTFOUND, if the requested node does not exist 347 | * -FDT_ERR_BADMAGIC, 348 | * -FDT_ERR_BADVERSION, 349 | * -FDT_ERR_BADSTATE, 350 | * -FDT_ERR_BADSTRUCTURE, 351 | * -FDT_ERR_TRUNCATED, standard meanings. 352 | */ 353 | int fdt_path_offset(const void *fdt, const char *path); 354 | 355 | /** 356 | * fdt_get_name - retrieve the name of a given node 357 | * @fdt: pointer to the device tree blob 358 | * @nodeoffset: structure block offset of the starting node 359 | * @lenp: pointer to an integer variable (will be overwritten) or NULL 360 | * 361 | * fdt_get_name() retrieves the name (including unit address) of the 362 | * device tree node at structure block offset nodeoffset. If lenp is 363 | * non-NULL, the length of this name is also returned, in the integer 364 | * pointed to by lenp. 365 | * 366 | * returns: 367 | * pointer to the node's name, on success 368 | * If lenp is non-NULL, *lenp contains the length of that name (>=0) 369 | * NULL, on error 370 | * if lenp is non-NULL *lenp contains an error code (<0): 371 | * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag 372 | * -FDT_ERR_BADMAGIC, 373 | * -FDT_ERR_BADVERSION, 374 | * -FDT_ERR_BADSTATE, standard meanings 375 | */ 376 | const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); 377 | 378 | /** 379 | * fdt_get_property_namelen - find a property based on substring 380 | * @fdt: pointer to the device tree blob 381 | * @nodeoffset: offset of the node whose property to find 382 | * @name: name of the property to find 383 | * @namelen: number of characters of name to consider 384 | * @lenp: pointer to an integer variable (will be overwritten) or NULL 385 | * 386 | * Identical to fdt_get_property_namelen(), but only examine the first 387 | * namelen characters of name for matching the property name. 388 | */ 389 | const struct fdt_property *fdt_get_property_namelen(const void *fdt, 390 | int nodeoffset, 391 | const char *name, 392 | int namelen, int *lenp); 393 | 394 | /** 395 | * fdt_get_property - find a given property in a given node 396 | * @fdt: pointer to the device tree blob 397 | * @nodeoffset: offset of the node whose property to find 398 | * @name: name of the property to find 399 | * @lenp: pointer to an integer variable (will be overwritten) or NULL 400 | * 401 | * fdt_get_property() retrieves a pointer to the fdt_property 402 | * structure within the device tree blob corresponding to the property 403 | * named 'name' of the node at offset nodeoffset. If lenp is 404 | * non-NULL, the length of the property value is also returned, in the 405 | * integer pointed to by lenp. 406 | * 407 | * returns: 408 | * pointer to the structure representing the property 409 | * if lenp is non-NULL, *lenp contains the length of the property 410 | * value (>=0) 411 | * NULL, on error 412 | * if lenp is non-NULL, *lenp contains an error code (<0): 413 | * -FDT_ERR_NOTFOUND, node does not have named property 414 | * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag 415 | * -FDT_ERR_BADMAGIC, 416 | * -FDT_ERR_BADVERSION, 417 | * -FDT_ERR_BADSTATE, 418 | * -FDT_ERR_BADSTRUCTURE, 419 | * -FDT_ERR_TRUNCATED, standard meanings 420 | */ 421 | const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, 422 | const char *name, int *lenp); 423 | static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, 424 | const char *name, 425 | int *lenp) 426 | { 427 | return (struct fdt_property *)(uintptr_t) 428 | fdt_get_property(fdt, nodeoffset, name, lenp); 429 | } 430 | 431 | /** 432 | * fdt_getprop_namelen - get property value based on substring 433 | * @fdt: pointer to the device tree blob 434 | * @nodeoffset: offset of the node whose property to find 435 | * @name: name of the property to find 436 | * @namelen: number of characters of name to consider 437 | * @lenp: pointer to an integer variable (will be overwritten) or NULL 438 | * 439 | * Identical to fdt_getprop(), but only examine the first namelen 440 | * characters of name for matching the property name. 441 | */ 442 | const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 443 | const char *name, int namelen, int *lenp); 444 | 445 | /** 446 | * fdt_getprop - retrieve the value of a given property 447 | * @fdt: pointer to the device tree blob 448 | * @nodeoffset: offset of the node whose property to find 449 | * @name: name of the property to find 450 | * @lenp: pointer to an integer variable (will be overwritten) or NULL 451 | * 452 | * fdt_getprop() retrieves a pointer to the value of the property 453 | * named 'name' of the node at offset nodeoffset (this will be a 454 | * pointer to within the device blob itself, not a copy of the value). 455 | * If lenp is non-NULL, the length of the property value is also 456 | * returned, in the integer pointed to by lenp. 457 | * 458 | * returns: 459 | * pointer to the property's value 460 | * if lenp is non-NULL, *lenp contains the length of the property 461 | * value (>=0) 462 | * NULL, on error 463 | * if lenp is non-NULL, *lenp contains an error code (<0): 464 | * -FDT_ERR_NOTFOUND, node does not have named property 465 | * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag 466 | * -FDT_ERR_BADMAGIC, 467 | * -FDT_ERR_BADVERSION, 468 | * -FDT_ERR_BADSTATE, 469 | * -FDT_ERR_BADSTRUCTURE, 470 | * -FDT_ERR_TRUNCATED, standard meanings 471 | */ 472 | const void *fdt_getprop(const void *fdt, int nodeoffset, 473 | const char *name, int *lenp); 474 | static inline void *fdt_getprop_w(void *fdt, int nodeoffset, 475 | const char *name, int *lenp) 476 | { 477 | return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); 478 | } 479 | 480 | /** 481 | * fdt_get_phandle - retrieve the phandle of a given node 482 | * @fdt: pointer to the device tree blob 483 | * @nodeoffset: structure block offset of the node 484 | * 485 | * fdt_get_phandle() retrieves the phandle of the device tree node at 486 | * structure block offset nodeoffset. 487 | * 488 | * returns: 489 | * the phandle of the node at nodeoffset, on success (!= 0, != -1) 490 | * 0, if the node has no phandle, or another error occurs 491 | */ 492 | uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); 493 | 494 | /** 495 | * fdt_get_alias_namelen - get alias based on substring 496 | * @fdt: pointer to the device tree blob 497 | * @name: name of the alias th look up 498 | * @namelen: number of characters of name to consider 499 | * 500 | * Identical to fdt_get_alias(), but only examine the first namelen 501 | * characters of name for matching the alias name. 502 | */ 503 | const char *fdt_get_alias_namelen(const void *fdt, 504 | const char *name, int namelen); 505 | 506 | /** 507 | * fdt_get_alias - retreive the path referenced by a given alias 508 | * @fdt: pointer to the device tree blob 509 | * @name: name of the alias th look up 510 | * 511 | * fdt_get_alias() retrieves the value of a given alias. That is, the 512 | * value of the property named 'name' in the node /aliases. 513 | * 514 | * returns: 515 | * a pointer to the expansion of the alias named 'name', of it exists 516 | * NULL, if the given alias or the /aliases node does not exist 517 | */ 518 | const char *fdt_get_alias(const void *fdt, const char *name); 519 | 520 | /** 521 | * fdt_get_path - determine the full path of a node 522 | * @fdt: pointer to the device tree blob 523 | * @nodeoffset: offset of the node whose path to find 524 | * @buf: character buffer to contain the returned path (will be overwritten) 525 | * @buflen: size of the character buffer at buf 526 | * 527 | * fdt_get_path() computes the full path of the node at offset 528 | * nodeoffset, and records that path in the buffer at buf. 529 | * 530 | * NOTE: This function is expensive, as it must scan the device tree 531 | * structure from the start to nodeoffset. 532 | * 533 | * returns: 534 | * 0, on success 535 | * buf contains the absolute path of the node at 536 | * nodeoffset, as a NUL-terminated string. 537 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 538 | * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) 539 | * characters and will not fit in the given buffer. 540 | * -FDT_ERR_BADMAGIC, 541 | * -FDT_ERR_BADVERSION, 542 | * -FDT_ERR_BADSTATE, 543 | * -FDT_ERR_BADSTRUCTURE, standard meanings 544 | */ 545 | int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); 546 | 547 | /** 548 | * fdt_supernode_atdepth_offset - find a specific ancestor of a node 549 | * @fdt: pointer to the device tree blob 550 | * @nodeoffset: offset of the node whose parent to find 551 | * @supernodedepth: depth of the ancestor to find 552 | * @nodedepth: pointer to an integer variable (will be overwritten) or NULL 553 | * 554 | * fdt_supernode_atdepth_offset() finds an ancestor of the given node 555 | * at a specific depth from the root (where the root itself has depth 556 | * 0, its immediate subnodes depth 1 and so forth). So 557 | * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); 558 | * will always return 0, the offset of the root node. If the node at 559 | * nodeoffset has depth D, then: 560 | * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); 561 | * will return nodeoffset itself. 562 | * 563 | * NOTE: This function is expensive, as it must scan the device tree 564 | * structure from the start to nodeoffset. 565 | * 566 | * returns: 567 | 568 | * structure block offset of the node at node offset's ancestor 569 | * of depth supernodedepth (>=0), on success 570 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 571 | * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset 572 | * -FDT_ERR_BADMAGIC, 573 | * -FDT_ERR_BADVERSION, 574 | * -FDT_ERR_BADSTATE, 575 | * -FDT_ERR_BADSTRUCTURE, standard meanings 576 | */ 577 | int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 578 | int supernodedepth, int *nodedepth); 579 | 580 | /** 581 | * fdt_node_depth - find the depth of a given node 582 | * @fdt: pointer to the device tree blob 583 | * @nodeoffset: offset of the node whose parent to find 584 | * 585 | * fdt_node_depth() finds the depth of a given node. The root node 586 | * has depth 0, its immediate subnodes depth 1 and so forth. 587 | * 588 | * NOTE: This function is expensive, as it must scan the device tree 589 | * structure from the start to nodeoffset. 590 | * 591 | * returns: 592 | * depth of the node at nodeoffset (>=0), on success 593 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 594 | * -FDT_ERR_BADMAGIC, 595 | * -FDT_ERR_BADVERSION, 596 | * -FDT_ERR_BADSTATE, 597 | * -FDT_ERR_BADSTRUCTURE, standard meanings 598 | */ 599 | int fdt_node_depth(const void *fdt, int nodeoffset); 600 | 601 | /** 602 | * fdt_parent_offset - find the parent of a given node 603 | * @fdt: pointer to the device tree blob 604 | * @nodeoffset: offset of the node whose parent to find 605 | * 606 | * fdt_parent_offset() locates the parent node of a given node (that 607 | * is, it finds the offset of the node which contains the node at 608 | * nodeoffset as a subnode). 609 | * 610 | * NOTE: This function is expensive, as it must scan the device tree 611 | * structure from the start to nodeoffset, *twice*. 612 | * 613 | * returns: 614 | * structure block offset of the parent of the node at nodeoffset 615 | * (>=0), on success 616 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 617 | * -FDT_ERR_BADMAGIC, 618 | * -FDT_ERR_BADVERSION, 619 | * -FDT_ERR_BADSTATE, 620 | * -FDT_ERR_BADSTRUCTURE, standard meanings 621 | */ 622 | int fdt_parent_offset(const void *fdt, int nodeoffset); 623 | 624 | /** 625 | * fdt_node_offset_by_prop_value - find nodes with a given property value 626 | * @fdt: pointer to the device tree blob 627 | * @startoffset: only find nodes after this offset 628 | * @propname: property name to check 629 | * @propval: property value to search for 630 | * @proplen: length of the value in propval 631 | * 632 | * fdt_node_offset_by_prop_value() returns the offset of the first 633 | * node after startoffset, which has a property named propname whose 634 | * value is of length proplen and has value equal to propval; or if 635 | * startoffset is -1, the very first such node in the tree. 636 | * 637 | * To iterate through all nodes matching the criterion, the following 638 | * idiom can be used: 639 | * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, 640 | * propval, proplen); 641 | * while (offset != -FDT_ERR_NOTFOUND) { 642 | * // other code here 643 | * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, 644 | * propval, proplen); 645 | * } 646 | * 647 | * Note the -1 in the first call to the function, if 0 is used here 648 | * instead, the function will never locate the root node, even if it 649 | * matches the criterion. 650 | * 651 | * returns: 652 | * structure block offset of the located node (>= 0, >startoffset), 653 | * on success 654 | * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the 655 | * tree after startoffset 656 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 657 | * -FDT_ERR_BADMAGIC, 658 | * -FDT_ERR_BADVERSION, 659 | * -FDT_ERR_BADSTATE, 660 | * -FDT_ERR_BADSTRUCTURE, standard meanings 661 | */ 662 | int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 663 | const char *propname, 664 | const void *propval, int proplen); 665 | 666 | /** 667 | * fdt_node_offset_by_phandle - find the node with a given phandle 668 | * @fdt: pointer to the device tree blob 669 | * @phandle: phandle value 670 | * 671 | * fdt_node_offset_by_phandle() returns the offset of the node 672 | * which has the given phandle value. If there is more than one node 673 | * in the tree with the given phandle (an invalid tree), results are 674 | * undefined. 675 | * 676 | * returns: 677 | * structure block offset of the located node (>= 0), on success 678 | * -FDT_ERR_NOTFOUND, no node with that phandle exists 679 | * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) 680 | * -FDT_ERR_BADMAGIC, 681 | * -FDT_ERR_BADVERSION, 682 | * -FDT_ERR_BADSTATE, 683 | * -FDT_ERR_BADSTRUCTURE, standard meanings 684 | */ 685 | int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); 686 | 687 | /** 688 | * fdt_node_check_compatible: check a node's compatible property 689 | * @fdt: pointer to the device tree blob 690 | * @nodeoffset: offset of a tree node 691 | * @compatible: string to match against 692 | * 693 | * 694 | * fdt_node_check_compatible() returns 0 if the given node contains a 695 | * 'compatible' property with the given string as one of its elements, 696 | * it returns non-zero otherwise, or on error. 697 | * 698 | * returns: 699 | * 0, if the node has a 'compatible' property listing the given string 700 | * 1, if the node has a 'compatible' property, but it does not list 701 | * the given string 702 | * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property 703 | * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag 704 | * -FDT_ERR_BADMAGIC, 705 | * -FDT_ERR_BADVERSION, 706 | * -FDT_ERR_BADSTATE, 707 | * -FDT_ERR_BADSTRUCTURE, standard meanings 708 | */ 709 | int fdt_node_check_compatible(const void *fdt, int nodeoffset, 710 | const char *compatible); 711 | 712 | /** 713 | * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value 714 | * @fdt: pointer to the device tree blob 715 | * @startoffset: only find nodes after this offset 716 | * @compatible: 'compatible' string to match against 717 | * 718 | * fdt_node_offset_by_compatible() returns the offset of the first 719 | * node after startoffset, which has a 'compatible' property which 720 | * lists the given compatible string; or if startoffset is -1, the 721 | * very first such node in the tree. 722 | * 723 | * To iterate through all nodes matching the criterion, the following 724 | * idiom can be used: 725 | * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); 726 | * while (offset != -FDT_ERR_NOTFOUND) { 727 | * // other code here 728 | * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); 729 | * } 730 | * 731 | * Note the -1 in the first call to the function, if 0 is used here 732 | * instead, the function will never locate the root node, even if it 733 | * matches the criterion. 734 | * 735 | * returns: 736 | * structure block offset of the located node (>= 0, >startoffset), 737 | * on success 738 | * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the 739 | * tree after startoffset 740 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 741 | * -FDT_ERR_BADMAGIC, 742 | * -FDT_ERR_BADVERSION, 743 | * -FDT_ERR_BADSTATE, 744 | * -FDT_ERR_BADSTRUCTURE, standard meanings 745 | */ 746 | int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 747 | const char *compatible); 748 | 749 | /** 750 | * fdt_node_offset_by_dtype - find nodes with a given 'device_type' value 751 | * @fdt: pointer to the device tree blob 752 | * @startoffset: only find nodes after this offset 753 | * @dtype: 'compatible' string to match against 754 | * 755 | * fdt_node_offset_by_dtype() returns the offset of the first 756 | * node after startoffset, which has a 'device_type' property which 757 | * lists the given type string; or if startoffset is -1, the 758 | * very first such node in the tree. 759 | * 760 | * To iterate through all nodes matching the criterion, the following 761 | * idiom can be used: 762 | * offset = fdt_node_offset_by_dtype(fdt, -1, "cpu"); 763 | * while (offset != -FDT_ERR_NOTFOUND) { 764 | * // other code here 765 | * offset = fdt_node_offset_by_dtype(fdt, offset, "cpu"); 766 | * } 767 | * 768 | * Note the -1 in the first call to the function, if 0 is used here 769 | * instead, the function will never locate the root node, even if it 770 | * matches the criterion. 771 | * 772 | * returns: 773 | * structure block offset of the located node (>= 0, >startoffset), 774 | * on success 775 | * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the 776 | * tree after startoffset 777 | * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag 778 | * -FDT_ERR_BADMAGIC, 779 | * -FDT_ERR_BADVERSION, 780 | * -FDT_ERR_BADSTATE, 781 | * -FDT_ERR_BADSTRUCTURE, standard meanings 782 | */ 783 | int fdt_node_offset_by_dtype(const void *fdt, int startoffset, 784 | const char *dtype); 785 | 786 | /**********************************************************************/ 787 | /* Debugging / informational functions */ 788 | /**********************************************************************/ 789 | 790 | const char * __attrconst fdt_strerror(int errval); 791 | 792 | #endif /* _LIBFDT_H */ 793 | -------------------------------------------------------------------------------- /libfdt_env.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBFDT_ENV_H 2 | #define _LIBFDT_ENV_H 3 | 4 | #include 5 | #include 6 | //#include 7 | 8 | #define fdt32_to_cpu(x) be32_to_cpu(x) 9 | #define cpu_to_fdt32(x) fdt32_to_cpu(x) 10 | 11 | #define fdt64_to_cpu(x) be64_to_cpu(x) 12 | #define cpu_to_fdt64(x) fdt64_to_cpu(x) 13 | 14 | #endif /* _LIBFDT_ENV_H */ 15 | -------------------------------------------------------------------------------- /libfdt_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBFDT_INTERNAL_H 2 | #define _LIBFDT_INTERNAL_H 3 | /* 4 | * libfdt - Flat Device Tree manipulation 5 | * Copyright (C) 2006 David Gibson, IBM Corporation. 6 | * 7 | * libfdt is dual licensed: you can use it either under the terms of 8 | * the GPL, or the BSD license, at your option. 9 | * 10 | * a) This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU General Public License as 12 | * published by the Free Software Foundation; either version 2 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public 21 | * License along with this library; if not, write to the Free 22 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 23 | * MA 02110-1301 USA 24 | * 25 | * Alternatively, 26 | * 27 | * b) Redistribution and use in source and binary forms, with or 28 | * without modification, are permitted provided that the following 29 | * conditions are met: 30 | * 31 | * 1. Redistributions of source code must retain the above 32 | * copyright notice, this list of conditions and the following 33 | * disclaimer. 34 | * 2. Redistributions in binary form must reproduce the above 35 | * copyright notice, this list of conditions and the following 36 | * disclaimer in the documentation and/or other materials 37 | * provided with the distribution. 38 | * 39 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 40 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 41 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 42 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 44 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 49 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 50 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 51 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | */ 53 | #include 54 | 55 | #define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) 56 | #define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) 57 | 58 | #define FDT_CHECK_HEADER(fdt) \ 59 | { \ 60 | int err; \ 61 | if ((err = fdt_check_header(fdt)) != 0) \ 62 | return err; \ 63 | } 64 | 65 | int _fdt_check_node_offset(const void *fdt, int offset); 66 | const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); 67 | int _fdt_node_end_offset(void *fdt, int nodeoffset); 68 | 69 | static inline const void *_fdt_offset_ptr(const void *fdt, int offset) 70 | { 71 | return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; 72 | } 73 | 74 | static inline void *_fdt_offset_ptr_w(void *fdt, int offset) 75 | { 76 | return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); 77 | } 78 | 79 | static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) 80 | { 81 | const struct fdt_reserve_entry *rsv_table = 82 | (const struct fdt_reserve_entry *) 83 | ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); 84 | 85 | return rsv_table + n; 86 | } 87 | static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) 88 | { 89 | return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); 90 | } 91 | 92 | #define FDT_SW_MAGIC (~FDT_MAGIC) 93 | 94 | #endif /* _LIBFDT_INTERNAL_H */ 95 | -------------------------------------------------------------------------------- /lmb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Logical memory blocks. 3 | * 4 | * Copyright (C) 2001 Peter Bergner, IBM Corp. 5 | * Copyright (C) 2018 Andrei Warkentin 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | static char * 26 | lmb_type(lmb_type_t type) 27 | { 28 | switch(type) { 29 | case LMB_FREE: 30 | return "FREE"; 31 | case LMB_BOOT: 32 | return "BOOT"; 33 | case LMB_RUNTIME: 34 | return "RUNTIME"; 35 | case LMB_MMIO: 36 | return "MMIO"; 37 | } 38 | 39 | return "INVALID"; 40 | } 41 | 42 | void 43 | lmb_dump_all(struct lmb *lmb) 44 | { 45 | unsigned long i; 46 | 47 | for (i=0; i < lmb->memory.cnt ;i++) { 48 | printk(" memory.reg[0x%lx] = 0x%016lx-0x%016lx (%.4s, %s)\n", i, 49 | lmb->memory.region[i].base, 50 | lmb->memory.region[i].base + 51 | lmb->memory.region[i].size - 1, 52 | (char *) &lmb->memory.region[i].tag, 53 | lmb_type(lmb->memory.region[i].type)); 54 | } 55 | 56 | for (i=0; i < lmb->reserved.cnt ;i++) { 57 | printk(" reserved.reg[0x%lx] = 0x%016lx @ 0x%016lx (%.4s, %s)\n", i, 58 | lmb->reserved.region[i].base, 59 | lmb->reserved.region[i].base + 60 | lmb->reserved.region[i].size - 1, 61 | (char *) &lmb->reserved.region[i].tag, 62 | lmb_type(lmb->reserved.region[i].type)); 63 | } 64 | } 65 | 66 | static long 67 | lmb_addrs_overlap(phys_addr_t base1, 68 | size_t size1, 69 | phys_addr_t base2, 70 | size_t size2) 71 | { 72 | return ((base1 < (base2+size2)) && (base2 < (base1+size1))); 73 | } 74 | 75 | static long 76 | lmb_addrs_adjacent(phys_addr_t base1, 77 | size_t size1, 78 | phys_addr_t base2, 79 | size_t size2) 80 | { 81 | if (base2 == base1 + size1) { 82 | return 1; 83 | } else if (base1 == base2 + size2) { 84 | return -1; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | static long 91 | lmb_regions_adjacent(struct lmb_region *rgn, 92 | unsigned long r1, 93 | unsigned long r2) 94 | { 95 | phys_addr_t base1 = rgn->region[r1].base; 96 | size_t size1 = rgn->region[r1].size; 97 | phys_addr_t base2 = rgn->region[r2].base; 98 | size_t size2 = rgn->region[r2].size; 99 | 100 | return lmb_addrs_adjacent(base1, size1, base2, size2); 101 | } 102 | 103 | static void 104 | lmb_remove_region(struct lmb_region *rgn, 105 | unsigned long r) 106 | { 107 | unsigned long i; 108 | 109 | for (i = r; i < rgn->cnt - 1; i++) { 110 | rgn->region[i].base = rgn->region[i + 1].base; 111 | rgn->region[i].size = rgn->region[i + 1].size; 112 | rgn->region[i].tag = rgn->region[i + 1].tag; 113 | rgn->region[i].type = rgn->region[i + 1].type; 114 | } 115 | 116 | rgn->cnt--; 117 | } 118 | 119 | /* 120 | * Assumption: base addr of region 1 < base addr of region 2 121 | * and tag and type are matching 122 | */ 123 | static void 124 | lmb_coalesce_regions(struct lmb_region *rgn, 125 | unsigned long r1, 126 | unsigned long r2) 127 | { 128 | rgn->region[r1].size += rgn->region[r2].size; 129 | 130 | lmb_remove_region(rgn, r2); 131 | } 132 | 133 | void 134 | lmb_init(struct lmb *lmb) 135 | { 136 | /* 137 | * Create a dummy zero size LMB which will get coalesced away later. 138 | * This simplifies the lmb_add() code below... 139 | */ 140 | lmb->memory.region[0].base = 0; 141 | lmb->memory.region[0].size = 0; 142 | lmb->memory.region[0].type = LMB_INVALID; 143 | lmb->memory.region[0].tag = LMB_TAG_NONE; 144 | lmb->memory.cnt = 1; 145 | 146 | /* Ditto. */ 147 | lmb->reserved.region[0].base = 0; 148 | lmb->reserved.region[0].size = 0; 149 | lmb->reserved.region[0].type = LMB_INVALID; 150 | lmb->reserved.region[0].tag = LMB_TAG_NONE; 151 | lmb->reserved.cnt = 1; 152 | } 153 | 154 | static long 155 | lmb_add_region(struct lmb_region *rgn, 156 | phys_addr_t base, 157 | size_t size, 158 | lmb_type_t type, 159 | lmb_tag_t tag) 160 | { 161 | unsigned long coalesced = 0; 162 | long adjacent, i; 163 | 164 | if ((rgn->cnt == 1) && (rgn->region[0].size == 0)) { 165 | rgn->region[0].base = base; 166 | rgn->region[0].size = size; 167 | rgn->region[0].tag = tag; 168 | rgn->region[0].type = type; 169 | return 0; 170 | } 171 | 172 | /* First try and coalesce this LMB with another. */ 173 | for (i=0; i < rgn->cnt; i++) { 174 | phys_addr_t rgnbase = rgn->region[i].base; 175 | size_t rgnsize = rgn->region[i].size; 176 | 177 | if (lmb_addrs_overlap(base, size, rgnbase, rgnsize)) { 178 | 179 | /* No overlapping with existing ones. */ 180 | return -1; 181 | } 182 | 183 | adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize); 184 | if ( adjacent > 0 ) { 185 | if (tag == rgn->region[i].tag && 186 | type == rgn->region[i].type) { 187 | rgn->region[i].base -= size; 188 | rgn->region[i].size += size; 189 | coalesced++; 190 | } 191 | break; 192 | } 193 | else if ( adjacent < 0 ) { 194 | if (tag == rgn->region[i].tag && 195 | type == rgn->region[i].type) { 196 | rgn->region[i].size += size; 197 | coalesced++; 198 | } 199 | break; 200 | } 201 | } 202 | 203 | if ((i < rgn->cnt-1) && 204 | lmb_regions_adjacent(rgn, i, i+1) && 205 | rgn->region[i].tag == rgn->region[i + 1].tag && 206 | rgn->region[i].type == rgn->region[i + 1].type) { 207 | lmb_coalesce_regions(rgn, i, i+1); 208 | coalesced++; 209 | } 210 | 211 | if (coalesced) { 212 | return coalesced; 213 | } 214 | 215 | if (rgn->cnt >= MAX_LMB_REGIONS) { 216 | return -1; 217 | } 218 | 219 | /* Couldn't coalesce the LMB, so add it to the sorted table. */ 220 | for (i = rgn->cnt-1; i >= 0; i--) { 221 | if (base < rgn->region[i].base) { 222 | rgn->region[i+1].base = rgn->region[i].base; 223 | rgn->region[i+1].size = rgn->region[i].size; 224 | rgn->region[i+1].type = rgn->region[i].type; 225 | rgn->region[i+1].tag = rgn->region[i].tag; 226 | } else { 227 | rgn->region[i+1].base = base; 228 | rgn->region[i+1].size = size; 229 | rgn->region[i+1].tag = tag; 230 | rgn->region[i+1].type = type; 231 | break; 232 | } 233 | } 234 | 235 | if (base < rgn->region[0].base) { 236 | rgn->region[0].base = base; 237 | rgn->region[0].size = size; 238 | rgn->region[0].tag = tag; 239 | rgn->region[0].type = type; 240 | } 241 | 242 | rgn->cnt++; 243 | 244 | return 0; 245 | } 246 | 247 | long 248 | lmb_add(struct lmb *lmb, 249 | phys_addr_t base, 250 | size_t size, 251 | lmb_tag_t tag) 252 | { 253 | struct lmb_region *_rgn = &(lmb->memory); 254 | 255 | return lmb_add_region(_rgn, base, size, LMB_FREE, tag); 256 | } 257 | 258 | long 259 | lmb_add_mmio(struct lmb *lmb, 260 | phys_addr_t base, 261 | size_t size, 262 | lmb_tag_t tag) 263 | { 264 | long ret; 265 | struct lmb_region *_rgn = &(lmb->memory); 266 | 267 | ret = lmb_add_region(_rgn, base, size, LMB_MMIO, tag); 268 | if (ret == 0) { 269 | lmb_add_region(&(lmb->reserved), base, size, LMB_MMIO, tag); 270 | } 271 | 272 | return ret; 273 | } 274 | 275 | static phys_addr_t 276 | lmb_align_down(phys_addr_t addr, 277 | size_t size) 278 | { 279 | return addr & ~(size - 1); 280 | } 281 | 282 | static phys_addr_t 283 | lmb_align_up(phys_addr_t addr, 284 | size_t size) 285 | { 286 | return (addr + size - 1) & ~(size - 1); 287 | } 288 | 289 | long 290 | lmb_free(struct lmb *lmb, 291 | phys_addr_t base, 292 | size_t size, 293 | size_t align) 294 | { 295 | struct lmb_region *rgn = &(lmb->reserved); 296 | phys_addr_t rgnbegin; 297 | phys_addr_t rgnend; 298 | phys_addr_t end; 299 | size = lmb_align_up(size, align); 300 | end = base + size; 301 | int i; 302 | 303 | rgnbegin = rgnend = 0; /* supress gcc warnings */ 304 | 305 | /* Find the region where (base, size) belongs to */ 306 | for (i=0; i < rgn->cnt; i++) { 307 | rgnbegin = rgn->region[i].base; 308 | rgnend = rgnbegin + rgn->region[i].size; 309 | 310 | if ((rgnbegin <= base) && (end <= rgnend)) 311 | break; 312 | } 313 | 314 | /* Didn't find the region */ 315 | if (i == rgn->cnt) { 316 | return -1; 317 | } 318 | 319 | if (rgn->region[i].type == LMB_MMIO) { 320 | return -1; 321 | } 322 | 323 | /* Check to see if we are removing entire region */ 324 | if ((rgnbegin == base) && (rgnend == end)) { 325 | lmb_remove_region(rgn, i); 326 | return 0; 327 | } 328 | 329 | /* Check to see if region is matching at the front */ 330 | if (rgnbegin == base) { 331 | rgn->region[i].base = end; 332 | rgn->region[i].size -= size; 333 | return 0; 334 | } 335 | 336 | /* Check to see if the region is matching at the end */ 337 | if (rgnend == end) { 338 | rgn->region[i].size -= size; 339 | return 0; 340 | } 341 | 342 | /* 343 | * We need to split the entry - adjust the current one to the 344 | * beginging of the hole and add the region after hole. 345 | */ 346 | rgn->region[i].size = base - rgn->region[i].base; 347 | return lmb_add_region(rgn, end, rgnend - end, 348 | rgn->region[i].type, 349 | rgn->region[i].tag); 350 | } 351 | 352 | long 353 | lmb_reserve(struct lmb *lmb, 354 | phys_addr_t base, 355 | size_t size, 356 | lmb_type_t type, 357 | lmb_tag_t tag) 358 | { 359 | struct lmb_region *_rgn = &(lmb->reserved); 360 | 361 | if (type != LMB_BOOT && 362 | type != LMB_RUNTIME) { 363 | return -1; 364 | } 365 | 366 | if (!lmb_is_known(lmb, base, size)) { 367 | return -1; 368 | } 369 | 370 | return lmb_add_region(_rgn, base, size, type, tag); 371 | } 372 | 373 | long 374 | lmb_overlaps_region(struct lmb_region *rgn, 375 | phys_addr_t base, 376 | size_t size) 377 | { 378 | unsigned long i; 379 | 380 | for (i=0; i < rgn->cnt; i++) { 381 | phys_addr_t rgnbase = rgn->region[i].base; 382 | size_t rgnsize = rgn->region[i].size; 383 | if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) { 384 | break; 385 | } 386 | } 387 | 388 | return (i < rgn->cnt) ? i : -1; 389 | } 390 | 391 | phys_addr_t 392 | lmb_alloc(struct lmb *lmb, 393 | size_t size, 394 | size_t align, 395 | lmb_type_t type, 396 | lmb_tag_t tag) 397 | { 398 | return lmb_alloc_base(lmb, size, align, LMB_ALLOC_ANYWHERE, type, tag); 399 | } 400 | 401 | phys_addr_t 402 | lmb_alloc_base(struct lmb *lmb, 403 | size_t size, 404 | size_t align, 405 | phys_addr_t max_addr, 406 | lmb_type_t type, 407 | lmb_tag_t tag) 408 | { 409 | phys_addr_t alloc; 410 | 411 | if (type == LMB_MMIO) { 412 | return 0; 413 | } 414 | 415 | alloc = __lmb_alloc_base(lmb, size, align, max_addr, type, tag); 416 | return alloc; 417 | } 418 | 419 | phys_addr_t 420 | __lmb_alloc_base(struct lmb *lmb, 421 | size_t size, 422 | size_t align, 423 | phys_addr_t max_addr, 424 | lmb_type_t type, 425 | lmb_tag_t tag) 426 | { 427 | long i, j; 428 | phys_addr_t base = 0; 429 | phys_addr_t res_base; 430 | size = lmb_align_up(size, align); 431 | 432 | for (i = lmb->memory.cnt-1; i >= 0; i--) { 433 | phys_addr_t lmbbase = lmb->memory.region[i].base; 434 | size_t lmbsize = lmb->memory.region[i].size; 435 | 436 | if (lmbsize < size) 437 | continue; 438 | if (max_addr == LMB_ALLOC_ANYWHERE) 439 | base = lmb_align_down(lmbbase + lmbsize - size, align); 440 | else if (lmbbase < max_addr) { 441 | base = min(lmbbase + lmbsize, max_addr); 442 | base = lmb_align_down(base - size, align); 443 | } else { 444 | continue; 445 | } 446 | 447 | while (base && lmbbase <= base) { 448 | j = lmb_overlaps_region(&lmb->reserved, base, 449 | size); 450 | if (j < 0) { 451 | 452 | /* This area isn't reserved, take it */ 453 | if (lmb_add_region(&lmb->reserved, base, 454 | size, 455 | type, 456 | tag) < 0) { 457 | return 0; 458 | } 459 | 460 | return base; 461 | } 462 | 463 | res_base = lmb->reserved.region[j].base; 464 | if (res_base < size) { 465 | break; 466 | } 467 | 468 | base = lmb_align_down(res_base - size, align); 469 | } 470 | } 471 | return 0; 472 | } 473 | 474 | int 475 | lmb_is_reserved(struct lmb *lmb, 476 | phys_addr_t addr) 477 | { 478 | int i; 479 | 480 | for (i = 0; i < lmb->reserved.cnt; i++) { 481 | phys_addr_t upper = lmb->reserved.region[i].base + 482 | lmb->reserved.region[i].size - 1; 483 | if ((addr >= lmb->reserved.region[i].base) && (addr <= upper)) 484 | return 1; 485 | } 486 | return 0; 487 | } 488 | 489 | int 490 | lmb_is_known(struct lmb *lmb, 491 | phys_addr_t addr, 492 | size_t size) 493 | { 494 | int i; 495 | 496 | for (i = 0; i < lmb->memory.cnt; i++) { 497 | phys_addr_t upper = lmb->memory.region[i].base + 498 | lmb->memory.region[i].size; 499 | if ((addr >= lmb->memory.region[i].base) && (addr <= upper - 1) && 500 | ((addr + size) >= lmb->memory.region[i].base) && 501 | ((addr + size) <= upper)) { 502 | return 1; 503 | } 504 | } 505 | 506 | return 0; 507 | } 508 | -------------------------------------------------------------------------------- /lmb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Logical memory blocks. 3 | * 4 | * Copyright (C) 2001 Peter Bergner, IBM Corp. 5 | * Copyright (C) 2018 Andrei Warkentin 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef LMB_H 23 | #define LMB_H 24 | 25 | /* Each lmb property has a tag associated with it. */ 26 | #define _LMB_TAG(a, b, c, d) \ 27 | ((uint32_t) d << 24 | (uint32_t) c << 16 | (uint32_t) b << 8 | a) 28 | 29 | #define LMB_TAG_NONE _LMB_TAG('N','O','N','E') 30 | 31 | #define LMB_TAG(string) \ 32 | (!string ? LMB_TAG_NONE : \ 33 | sizeof(string) > 4 ? _LMB_TAG(string[0], string[1], string[2], string[3]) : \ 34 | sizeof(string) > 3 ? _LMB_TAG(string[0], string[1], string[2], 0) : \ 35 | sizeof(string) > 2 ? _LMB_TAG(string[0], string[1], 0, 0) : \ 36 | sizeof(string) > 1 ? _LMB_TAG(string[0], 0, 0, 0) : \ 37 | LMB_TAG_NONE) 38 | 39 | typedef uint32_t lmb_tag_t; 40 | typedef uint32_t lmb_type_t; 41 | 42 | #define MAX_LMB_REGIONS 32 43 | 44 | struct lmb_property { 45 | phys_addr_t base; 46 | size_t size; 47 | lmb_tag_t tag; 48 | lmb_type_t type; 49 | 50 | /* Shouldn't ever be set to this. */ 51 | #define LMB_INVALID (0) 52 | 53 | /* Memory range is free. */ 54 | #define LMB_FREE (1) 55 | 56 | /* Memory can be freed after kernel boot. */ 57 | #define LMB_BOOT (2) 58 | 59 | /* Memory must be reserved and unused after kernel boot. */ 60 | #define LMB_RUNTIME (3) 61 | 62 | /* 63 | * I/O memory must be reserved and unused after kernel boot. 64 | * 65 | * Regions of this type can only be manipulated using 66 | * lmb_add_mmio - not allocated or freed. 67 | */ 68 | #define LMB_MMIO (4) 69 | }; 70 | 71 | struct lmb_region { 72 | unsigned long cnt; 73 | struct lmb_property region[MAX_LMB_REGIONS+1]; 74 | }; 75 | 76 | struct lmb { 77 | struct lmb_region memory; 78 | struct lmb_region reserved; 79 | }; 80 | 81 | extern struct lmb lmb; 82 | 83 | void lmb_init(struct lmb *lmb); 84 | long lmb_add(struct lmb *lmb, 85 | phys_addr_t base, 86 | size_t size, 87 | lmb_tag_t tag); 88 | long lmb_add_mmio(struct lmb *lmb, 89 | phys_addr_t base, 90 | size_t size, 91 | lmb_tag_t tag); 92 | long lmb_reserve(struct lmb *lmb, 93 | phys_addr_t base, 94 | size_t size, 95 | lmb_type_t type, 96 | lmb_tag_t tag); 97 | phys_addr_t lmb_alloc(struct lmb *lmb, 98 | size_t size, 99 | size_t align, 100 | lmb_type_t type, 101 | lmb_tag_t tag); 102 | 103 | #define LMB_ALLOC_ANYWHERE 0 104 | #define LMB_ALLOC_32BIT 0xffffffff 105 | phys_addr_t lmb_alloc_base(struct lmb *lmb, 106 | size_t size, 107 | size_t align, 108 | phys_addr_t max_addr, 109 | lmb_type_t type, 110 | lmb_tag_t tag); 111 | phys_addr_t __lmb_alloc_base(struct lmb *lmb, 112 | size_t size, 113 | size_t align, 114 | phys_addr_t max_addr, 115 | lmb_type_t type, 116 | lmb_tag_t tag); 117 | int lmb_is_reserved(struct lmb *lmb, phys_addr_t addr); 118 | int lmb_is_known(struct lmb *lmb, 119 | phys_addr_t addr, 120 | size_t size); 121 | long lmb_free(struct lmb *lmb, 122 | phys_addr_t base, 123 | size_t size, 124 | size_t align); 125 | 126 | void lmb_dump_all(struct lmb *lmb); 127 | 128 | long lmb_overlaps_region(struct lmb_region *rgn, 129 | phys_addr_t base, 130 | size_t size); 131 | 132 | static inline size_t 133 | lmb_size_bytes(struct lmb_region *type, unsigned long region_nr) 134 | { 135 | return type->region[region_nr].size; 136 | } 137 | 138 | #endif /* LMB_H */ 139 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | extern void fb_launch(void *fdt); 27 | 28 | struct lmb lmb; 29 | 30 | static uint64_t 31 | memparse(const char *ptr, char **retptr) 32 | { 33 | /* local pointer to end of parsed string */ 34 | char *endptr; 35 | 36 | uint64_t ret = simple_strtoull(ptr, &endptr, 0); 37 | 38 | switch (*endptr) { 39 | case 'G': 40 | case 'g': 41 | ret <<= 10; 42 | case 'M': 43 | case 'm': 44 | ret <<= 10; 45 | case 'K': 46 | case 'k': 47 | ret <<= 10; 48 | endptr++; 49 | default: 50 | break; 51 | } 52 | 53 | if (retptr) { 54 | *retptr = endptr; 55 | } 56 | 57 | return ret; 58 | } 59 | 60 | static int 61 | parse_memloc(const char *arg, 62 | uint64_t *psize, 63 | uint64_t *ploc) 64 | { 65 | char *at; 66 | unsigned long long size; 67 | 68 | size = memparse(arg, &at); 69 | if (*at != '@') { 70 | /* 71 | * Treat a single location as a page at that address. 72 | */ 73 | *ploc = size; 74 | *psize = PAGE_SIZE; 75 | return 0; 76 | } 77 | 78 | *ploc = memparse(at + 1, NULL); 79 | *psize = size; 80 | return 0; 81 | } 82 | 83 | static void 84 | arch_dump(void) 85 | { 86 | uint64_t el; 87 | uint64_t sctlr; 88 | 89 | ReadSysReg(el, CurrentEL); 90 | el = SPSR_2_EL(el); 91 | printk("EL%u ", el); 92 | 93 | if (el == 2) { 94 | ReadSysReg(sctlr, sctlr_el2); 95 | } else { 96 | ReadSysReg(sctlr, sctlr_el1); 97 | } 98 | 99 | if ((sctlr & SCTLR_M) != 0) { 100 | printk("MMU "); 101 | } 102 | if ((sctlr & SCTLR_A) != 0) { 103 | printk("A n"); 104 | } 105 | if ((sctlr & SCTLR_C) != 0) { 106 | printk("D "); 107 | } 108 | if ((sctlr & SCTLR_SA) != 0) { 109 | printk("SA "); 110 | } 111 | if ((sctlr & SCTLR_I) == 0) { 112 | printk("I "); 113 | } 114 | 115 | printk("\n"); 116 | } 117 | 118 | static void 119 | dump_cmdline(const char *s) 120 | { 121 | const char *n = s; 122 | 123 | if (s == NULL) { 124 | printk("No cmdline\n"); 125 | return; 126 | } 127 | 128 | printk("Cmdline: "); 129 | /* 130 | * Dump is word by word because printk buffer has a maximum size. 131 | */ 132 | while ((n = strchr (s, ' ')) != NULL) { 133 | printk("%.*s", n - s + 1, s); 134 | s = n + 1; 135 | } 136 | 137 | printk("%s\n", s); 138 | } 139 | 140 | static bool_t 141 | parse_range(const char *cmdline, 142 | const char *range_name, 143 | phys_addr_t *base, 144 | size_t *size) 145 | { 146 | const char *param; 147 | phys_addr_t b; 148 | size_t s; 149 | 150 | if (cmdline == NULL) { 151 | return false; 152 | } 153 | 154 | param = strstr(cmdline, range_name); 155 | if (param == NULL) { 156 | return false; 157 | } 158 | 159 | if (parse_memloc(param + strlen(range_name), &s, &b) != 0) { 160 | return false; 161 | } 162 | 163 | if (base != NULL) { 164 | *base = b; 165 | } 166 | 167 | if (size != NULL) { 168 | *size = s; 169 | } 170 | 171 | return true; 172 | } 173 | 174 | static bool_t 175 | parse_and_reserve_range(const char *cmdline, 176 | const char *range_name, 177 | lmb_type_t type, 178 | lmb_tag_t tag, 179 | phys_addr_t *base, 180 | size_t *size) 181 | { 182 | phys_addr_t b; 183 | size_t s; 184 | 185 | if (parse_range(cmdline, range_name, &b, &s)) { 186 | lmb_reserve(&lmb, b, s, type, tag); 187 | 188 | if (base != NULL) { 189 | *base = b; 190 | } 191 | 192 | if (size != NULL) { 193 | *size = s; 194 | } 195 | 196 | return true; 197 | } 198 | 199 | return false; 200 | } 201 | 202 | static const char * 203 | fdt_get_cmdline(void *fdt) 204 | { 205 | int node; 206 | 207 | if (fdt_check_header(fdt) < 0) { 208 | return NULL; 209 | } 210 | 211 | node = fdt_path_offset(fdt, "/chosen"); 212 | if (node < 0) { 213 | return NULL; 214 | } 215 | 216 | return fdt_getprop(fdt, node, "bootargs", NULL); 217 | } 218 | 219 | void 220 | main(void *fdt, uint32_t el) 221 | { 222 | int node; 223 | int resv_count; 224 | phys_addr_t base; 225 | uint64_t size; 226 | phys_addr_t fb_base; 227 | uint64_t fb_size; 228 | extern void *image_start; 229 | extern void *image_end; 230 | const char *cmdline = NULL; 231 | 232 | cmdline = fdt_get_cmdline(fdt); 233 | if (cmdline == NULL || 234 | !parse_range(cmdline, "tegra_fbmem=", &fb_base, 235 | &fb_size)) { 236 | /* 237 | * The 1.3 firmware default. 238 | * 239 | * 1.4 and 2.1 use 0x92ca8000. 240 | */ 241 | fb_base = 0x92ca6000; 242 | fb_size = CONFIG_VIDEO_VISIBLE_COLS * 243 | CONFIG_VIDEO_VISIBLE_ROWS * 244 | CONFIG_VIDEO_PIXEL_SIZE; 245 | } 246 | 247 | video_init(VP(fb_base)); 248 | printk("ShieldTV Loader (https://github.com/andreiw/shieldTV_loader) - "); 249 | arch_dump(); 250 | 251 | lmb_init(&lmb); 252 | 253 | dump_cmdline(cmdline); 254 | 255 | for (node = fdt_node_offset_by_dtype(fdt, -1, "memory"); 256 | node != -1; 257 | node = fdt_node_offset_by_dtype(fdt, node, "memory")) { 258 | int prop_len; 259 | const uint32_t *prop = fdt_getprop(fdt, node, "reg", &prop_len); 260 | 261 | BUG_ON_EX(prop == NULL, "node %d has no reg property", node); 262 | while (prop_len != 0) { 263 | base = fdt32_to_cpu(*(prop + 1)) | 264 | ((uint64_t) fdt32_to_cpu(*prop)) << 32; 265 | prop += 2; 266 | size = fdt32_to_cpu(*(prop + 1)) | 267 | ((uint64_t) fdt32_to_cpu(*prop)) << 32; 268 | prop += 2; 269 | lmb_add(&lmb, base, size, LMB_TAG("RAMR")); 270 | prop_len -= 16; 271 | } 272 | } 273 | 274 | /* 275 | * Not seen on the shield with my firmware version (2.1), 276 | * but just in case. 277 | */ 278 | parse_and_reserve_range(cmdline, "tsec=", LMB_RUNTIME, 279 | LMB_TAG("TSEC"), NULL, NULL); 280 | 281 | /* 282 | * Not seen on the shield with my firmware version (2.1), but 283 | * just in case. 284 | */ 285 | parse_and_reserve_range(cmdline, "vpr=", LMB_RUNTIME, 286 | LMB_TAG("VPRR"), NULL, NULL); 287 | 288 | /* 289 | * On my shield, above reported available memory ranges, but 290 | * just in case. 291 | */ 292 | parse_and_reserve_range(cmdline, "lp0_vec=", LMB_RUNTIME, 293 | LMB_TAG("LP0V"), NULL, NULL); 294 | 295 | /* 296 | * On my shield, above reported available memory ranges, but 297 | * just in case. 298 | */ 299 | parse_and_reserve_range(cmdline, "nvdumper_reserved=", LMB_RUNTIME, 300 | LMB_TAG("NVDR"), NULL, NULL); 301 | 302 | lmb_reserve(&lmb, fb_base, fb_size, 303 | LMB_RUNTIME, LMB_TAG("FBMM")); 304 | 305 | lmb_reserve(&lmb, (phys_addr_t) fdt, fdt_totalsize(fdt), LMB_BOOT, 306 | LMB_TAG("FDTB")); 307 | 308 | lmb_reserve(&lmb, (phys_addr_t) &image_start, 309 | UN(&image_end) - UN(&image_start), LMB_BOOT, 310 | LMB_TAG("LDRS")); 311 | 312 | resv_count = fdt_num_mem_rsv(fdt); 313 | for (node = 0; node < resv_count; node++) { 314 | phys_addr_t base; 315 | size_t size; 316 | 317 | fdt_get_mem_rsv(fdt, node, &base, &size); 318 | lmb_reserve(&lmb, base, size, LMB_BOOT, LMB_TAG("RESV")); 319 | } 320 | 321 | fb_launch(fdt); 322 | BUG(); 323 | } 324 | -------------------------------------------------------------------------------- /start.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | .globl asm_start 21 | .globl smc_call 22 | 23 | /* 24 | * x0 contains the FDT blob PA 25 | */ 26 | .section ".text" 27 | .globl image_end 28 | .globl image_start 29 | asm_start: 30 | b asm_real // branch to kernel start, magic 31 | .long 0 // reserved 32 | .quad image_start 33 | .quad 0 // reserved 34 | .quad 0 // reserved 35 | .quad 0 // reserved 36 | .quad 0 // reserved 37 | .quad 0 // reserved 38 | .byte 0x41 // Magic number, "ARM\x64" 39 | .byte 0x52 40 | .byte 0x4d 41 | .byte 0x64 42 | .word 0 // reserved 43 | asm_real: 44 | adr x1, image_start 45 | adr x2, rela_start 46 | adr x3, rela_end 47 | reloc_loop: 48 | cmp x2, x3 49 | bhs reloc_done 50 | 51 | // 52 | // struct rela_entry_t { 53 | // uint64_t offset; // relative offset 54 | // uint64_t info; // 0x403 == R_AARCH64_RELATIVE 55 | // uint64_t addend; // to adjust by actual loaded address 56 | // } rela_entry_t; 57 | // 58 | ldp x4, x5, [x2], #24 59 | cmp x5, #0x403 // R_AARCH64_RELATIVE 60 | bne reloc_loop 61 | ldr x5, [x2, #-8] 62 | add x5, x5, x1 // relies on link base being 0, so 63 | // no need to subtract link base 64 | str x5, [x4, x1] 65 | b reloc_loop 66 | reloc_done: 67 | load_stack: 68 | ldr x1, =stack_end 69 | mov sp, x1 70 | clear_bss: 71 | ldr x1, bss_start 72 | ldr x2, bss_end 73 | 1: cmp x1, x2 74 | b.hs go_to_c 75 | str xzr, [x1], #8 76 | b 1b 77 | go_to_c: 78 | bl main // x0 = fdt 79 | 1: b 1b 80 | 81 | smc_call: 82 | str x0, [sp, #-16]! // remember the inout array 83 | ldp x6, x7, [x0, #48] 84 | ldp x4, x5, [x0, #32] 85 | ldp x2, x3, [x0, #16] 86 | ldp x0, x1, [x0, #0] 87 | smc #0 88 | ldr x9, [sp], #16 // x9 <- inout array address 89 | stp x2, x3, [x9, #16] // SMC calls can return up 90 | stp x0, x1, [x9, #0] // to 4 values. 91 | ret 92 | .section .data 93 | .align 4 94 | stack: 95 | .fill 4096, 8, 0 96 | stack_end: 97 | 98 | -------------------------------------------------------------------------------- /string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrei Warkentin 3 | * 4 | * This program is free software ; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | /* 10 | * linux/lib/string.c 11 | * 12 | * Copyright (C) 1991, 1992 Linus Torvalds 13 | */ 14 | 15 | /* 16 | * stupid library routines.. The optimized versions should generally be found 17 | * as inline code in 18 | * 19 | * These are buggy as well.. 20 | * 21 | * * Fri Jun 25 1999, Ingo Oeser 22 | * - Added strsep() which will replace strtok() soon (because strsep() is 23 | * reentrant and should be faster). Use only strsep() in new code, please. 24 | * 25 | * * Sat Feb 09 2002, Jason Thomas , 26 | * Matthew Hawkins 27 | * - Kissed strtok() goodbye 28 | */ 29 | 30 | /* 31 | * Modified for AOS - Andrei Warkentin 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | #ifndef __HAVE_ARCH_STRNICMP 39 | /** 40 | * strnicmp - Case insensitive, length-limited string comparison 41 | * @s1: One string 42 | * @s2: The other string 43 | * @len: the maximum number of characters to compare 44 | */ 45 | int strnicmp(const char *s1, const char *s2, size_t len) 46 | { 47 | /* Yes, Virginia, it had better be unsigned */ 48 | unsigned char c1, c2; 49 | 50 | if (!len) 51 | return 0; 52 | 53 | do { 54 | c1 = *s1++; 55 | c2 = *s2++; 56 | if (!c1 || !c2) 57 | break; 58 | if (c1 == c2) 59 | continue; 60 | c1 = tolower(c1); 61 | c2 = tolower(c2); 62 | if (c1 != c2) 63 | break; 64 | } while (--len); 65 | return (int)c1 - (int)c2; 66 | } 67 | #endif 68 | 69 | #ifndef __HAVE_ARCH_STRCASECMP 70 | int strcasecmp(const char *s1, const char *s2) 71 | { 72 | int c1, c2; 73 | 74 | do { 75 | c1 = tolower(*s1++); 76 | c2 = tolower(*s2++); 77 | } while (c1 == c2 && c1 != 0); 78 | return c1 - c2; 79 | } 80 | #endif 81 | 82 | #ifndef __HAVE_ARCH_STRNCASECMP 83 | int strncasecmp(const char *s1, const char *s2, size_t n) 84 | { 85 | int c1, c2; 86 | 87 | do { 88 | c1 = tolower(*s1++); 89 | c2 = tolower(*s2++); 90 | } while ((--n > 0) && c1 == c2 && c1 != 0); 91 | return c1 - c2; 92 | } 93 | #endif 94 | 95 | #ifndef __HAVE_ARCH_STRCPY 96 | /** 97 | * strcpy - Copy a %NUL terminated string 98 | * @dest: Where to copy the string to 99 | * @src: Where to copy the string from 100 | */ 101 | #undef strcpy 102 | char *strcpy(char *dest, const char *src) 103 | { 104 | char *tmp = dest; 105 | 106 | while ((*dest++ = *src++) != '\0') 107 | /* nothing */; 108 | return tmp; 109 | } 110 | #endif 111 | 112 | #ifndef __HAVE_ARCH_STRNCPY 113 | /** 114 | * strncpy - Copy a length-limited, %NUL-terminated string 115 | * @dest: Where to copy the string to 116 | * @src: Where to copy the string from 117 | * @count: The maximum number of bytes to copy 118 | * 119 | * The result is not %NUL-terminated if the source exceeds 120 | * @count bytes. 121 | * 122 | * In the case where the length of @src is less than that of 123 | * count, the remainder of @dest will be padded with %NUL. 124 | * 125 | */ 126 | char *strncpy(char *dest, const char *src, size_t count) 127 | { 128 | char *tmp = dest; 129 | 130 | while (count) { 131 | if ((*tmp = *src) != 0) 132 | src++; 133 | tmp++; 134 | count--; 135 | } 136 | return dest; 137 | } 138 | #endif 139 | 140 | #ifndef __HAVE_ARCH_STRLCPY 141 | /** 142 | * strlcpy - Copy a %NUL terminated string into a sized buffer 143 | * @dest: Where to copy the string to 144 | * @src: Where to copy the string from 145 | * @size: size of destination buffer 146 | * 147 | * Compatible with *BSD: the result is always a valid 148 | * NUL-terminated string that fits in the buffer (unless, 149 | * of course, the buffer size is zero). It does not pad 150 | * out the result like strncpy() does. 151 | */ 152 | size_t strlcpy(char *dest, const char *src, size_t size) 153 | { 154 | size_t ret = strlen(src); 155 | 156 | if (size) { 157 | size_t len = (ret >= size) ? size - 1 : ret; 158 | memcpy(dest, src, len); 159 | dest[len] = '\0'; 160 | } 161 | return ret; 162 | } 163 | #endif 164 | 165 | #ifndef __HAVE_ARCH_STRCAT 166 | /** 167 | * strcat - Append one %NUL-terminated string to another 168 | * @dest: The string to be appended to 169 | * @src: The string to append to it 170 | */ 171 | #undef strcat 172 | char *strcat(char *dest, const char *src) 173 | { 174 | char *tmp = dest; 175 | 176 | while (*dest) 177 | dest++; 178 | while ((*dest++ = *src++) != '\0') 179 | ; 180 | return tmp; 181 | } 182 | #endif 183 | 184 | #ifndef __HAVE_ARCH_STRNCAT 185 | /** 186 | * strncat - Append a length-limited, %NUL-terminated string to another 187 | * @dest: The string to be appended to 188 | * @src: The string to append to it 189 | * @count: The maximum numbers of bytes to copy 190 | * 191 | * Note that in contrast to strncpy(), strncat() ensures the result is 192 | * terminated. 193 | */ 194 | char *strncat(char *dest, const char *src, size_t count) 195 | { 196 | char *tmp = dest; 197 | 198 | if (count) { 199 | while (*dest) 200 | dest++; 201 | while ((*dest++ = *src++) != 0) { 202 | if (--count == 0) { 203 | *dest = '\0'; 204 | break; 205 | } 206 | } 207 | } 208 | return tmp; 209 | } 210 | #endif 211 | 212 | #ifndef __HAVE_ARCH_STRLCAT 213 | /** 214 | * strlcat - Append a length-limited, %NUL-terminated string to another 215 | * @dest: The string to be appended to 216 | * @src: The string to append to it 217 | * @count: The size of the destination buffer. 218 | */ 219 | size_t strlcat(char *dest, const char *src, size_t count) 220 | { 221 | size_t dsize = strlen(dest); 222 | size_t len = strlen(src); 223 | size_t res = dsize + len; 224 | 225 | /* This would be a bug */ 226 | /* BUG_ON(dsize >= count); */ 227 | 228 | dest += dsize; 229 | count -= dsize; 230 | if (len >= count) 231 | len = count-1; 232 | memcpy(dest, src, len); 233 | dest[len] = 0; 234 | return res; 235 | } 236 | #endif 237 | 238 | #ifndef __HAVE_ARCH_STRCMP 239 | /** 240 | * strcmp - Compare two strings 241 | * @cs: One string 242 | * @ct: Another string 243 | */ 244 | #undef strcmp 245 | int strcmp(const char *cs, const char *ct) 246 | { 247 | unsigned char c1, c2; 248 | 249 | while (1) { 250 | c1 = *cs++; 251 | c2 = *ct++; 252 | if (c1 != c2) 253 | return c1 < c2 ? -1 : 1; 254 | if (!c1) 255 | break; 256 | } 257 | return 0; 258 | } 259 | #endif 260 | 261 | #ifndef __HAVE_ARCH_STRNCMP 262 | /** 263 | * strncmp - Compare two length-limited strings 264 | * @cs: One string 265 | * @ct: Another string 266 | * @count: The maximum number of bytes to compare 267 | */ 268 | int strncmp(const char *cs, const char *ct, size_t count) 269 | { 270 | unsigned char c1, c2; 271 | 272 | while (count) { 273 | c1 = *cs++; 274 | c2 = *ct++; 275 | if (c1 != c2) 276 | return c1 < c2 ? -1 : 1; 277 | if (!c1) 278 | break; 279 | count--; 280 | } 281 | return 0; 282 | } 283 | #endif 284 | 285 | #ifndef __HAVE_ARCH_STRCHR 286 | /** 287 | * strchr - Find the first occurrence of a character in a string 288 | * @s: The string to be searched 289 | * @c: The character to search for 290 | */ 291 | char *strchr(const char *s, int c) 292 | { 293 | for (; *s != (char)c; ++s) 294 | if (*s == '\0') 295 | return NULL; 296 | return (char *)s; 297 | } 298 | #endif 299 | 300 | #ifndef __HAVE_ARCH_STRRCHR 301 | /** 302 | * strrchr - Find the last occurrence of a character in a string 303 | * @s: The string to be searched 304 | * @c: The character to search for 305 | */ 306 | char *strrchr(const char *s, int c) 307 | { 308 | const char *p = s + strlen(s); 309 | do { 310 | if (*p == (char)c) 311 | return (char *)p; 312 | } while (--p >= s); 313 | return NULL; 314 | } 315 | #endif 316 | 317 | #ifndef __HAVE_ARCH_STRNCHR 318 | /** 319 | * strnchr - Find a character in a length limited string 320 | * @s: The string to be searched 321 | * @count: The number of characters to be searched 322 | * @c: The character to search for 323 | */ 324 | char *strnchr(const char *s, size_t count, int c) 325 | { 326 | for (; count-- && *s != '\0'; ++s) 327 | if (*s == (char)c) 328 | return (char *)s; 329 | return NULL; 330 | } 331 | #endif 332 | 333 | /** 334 | * skip_spaces - Removes leading whitespace from @str. 335 | * @str: The string to be stripped. 336 | * 337 | * Returns a pointer to the first non-whitespace character in @str. 338 | */ 339 | char *skip_spaces(const char *str) 340 | { 341 | while (isspace(*str)) 342 | ++str; 343 | return (char *)str; 344 | } 345 | 346 | /** 347 | * strim - Removes leading and trailing whitespace from @s. 348 | * @s: The string to be stripped. 349 | * 350 | * Note that the first trailing whitespace is replaced with a %NUL-terminator 351 | * in the given string @s. Returns a pointer to the first non-whitespace 352 | * character in @s. 353 | */ 354 | char *strim(char *s) 355 | { 356 | size_t size; 357 | char *end; 358 | 359 | s = skip_spaces(s); 360 | size = strlen(s); 361 | if (!size) 362 | return s; 363 | 364 | end = s + size - 1; 365 | while (end >= s && isspace(*end)) 366 | end--; 367 | *(end + 1) = '\0'; 368 | 369 | return s; 370 | } 371 | 372 | #ifndef __HAVE_ARCH_STRLEN 373 | /** 374 | * strlen - Find the length of a string 375 | * @s: The string to be sized 376 | */ 377 | size_t strlen(const char *s) 378 | { 379 | const char *sc; 380 | 381 | for (sc = s; *sc != '\0'; ++sc) 382 | /* nothing */; 383 | return sc - s; 384 | } 385 | #endif 386 | 387 | #ifndef __HAVE_ARCH_STRNLEN 388 | /** 389 | * strnlen - Find the length of a length-limited string 390 | * @s: The string to be sized 391 | * @count: The maximum number of bytes to search 392 | */ 393 | size_t strnlen(const char *s, size_t count) 394 | { 395 | const char *sc; 396 | 397 | for (sc = s; count-- && *sc != '\0'; ++sc) 398 | /* nothing */; 399 | return sc - s; 400 | } 401 | #endif 402 | 403 | #ifndef __HAVE_ARCH_STRSPN 404 | /** 405 | * strspn - Calculate the length of the initial substring of @s which only contain letters in @accept 406 | * @s: The string to be searched 407 | * @accept: The string to search for 408 | */ 409 | size_t strspn(const char *s, const char *accept) 410 | { 411 | const char *p; 412 | const char *a; 413 | size_t count = 0; 414 | 415 | for (p = s; *p != '\0'; ++p) { 416 | for (a = accept; *a != '\0'; ++a) { 417 | if (*p == *a) 418 | break; 419 | } 420 | if (*a == '\0') 421 | return count; 422 | ++count; 423 | } 424 | return count; 425 | } 426 | 427 | #endif 428 | 429 | #ifndef __HAVE_ARCH_STRCSPN 430 | /** 431 | * strcspn - Calculate the length of the initial substring of @s which does not contain letters in @reject 432 | * @s: The string to be searched 433 | * @reject: The string to avoid 434 | */ 435 | size_t strcspn(const char *s, const char *reject) 436 | { 437 | const char *p; 438 | const char *r; 439 | size_t count = 0; 440 | 441 | for (p = s; *p != '\0'; ++p) { 442 | for (r = reject; *r != '\0'; ++r) { 443 | if (*p == *r) 444 | return count; 445 | } 446 | ++count; 447 | } 448 | return count; 449 | } 450 | #endif 451 | 452 | #ifndef __HAVE_ARCH_STRPBRK 453 | /** 454 | * strpbrk - Find the first occurrence of a set of characters 455 | * @cs: The string to be searched 456 | * @ct: The characters to search for 457 | */ 458 | char *strpbrk(const char *cs, const char *ct) 459 | { 460 | const char *sc1, *sc2; 461 | 462 | for (sc1 = cs; *sc1 != '\0'; ++sc1) { 463 | for (sc2 = ct; *sc2 != '\0'; ++sc2) { 464 | if (*sc1 == *sc2) 465 | return (char *)sc1; 466 | } 467 | } 468 | return NULL; 469 | } 470 | #endif 471 | 472 | #ifndef __HAVE_ARCH_STRSEP 473 | /** 474 | * strsep - Split a string into tokens 475 | * @s: The string to be searched 476 | * @ct: The characters to search for 477 | * 478 | * strsep() updates @s to point after the token, ready for the next call. 479 | * 480 | * It returns empty tokens, too, behaving exactly like the libc function 481 | * of that name. In fact, it was stolen from glibc2 and de-fancy-fied. 482 | * Same semantics, slimmer shape. ;) 483 | */ 484 | char *strsep(char **s, const char *ct) 485 | { 486 | char *sbegin = *s; 487 | char *end; 488 | 489 | if (sbegin == NULL) 490 | return NULL; 491 | 492 | end = strpbrk(sbegin, ct); 493 | if (end) 494 | *end++ = '\0'; 495 | *s = end; 496 | return sbegin; 497 | } 498 | #endif 499 | 500 | #ifndef __HAVE_ARCH_MEMSET 501 | /** 502 | * memset - Fill a region of memory with the given value 503 | * @s: Pointer to the start of the area. 504 | * @c: The byte to fill the area with 505 | * @count: The size of the area. 506 | * 507 | * Do not use memset() to access IO space, use memset_io() instead. 508 | */ 509 | void *memset(void *s, int c, size_t count) 510 | { 511 | char *xs = s; 512 | 513 | while (count--) 514 | *xs++ = c; 515 | return s; 516 | } 517 | #endif 518 | 519 | #ifndef __HAVE_ARCH_MEMCPY 520 | /** 521 | * memcpy - Copy one area of memory to another 522 | * @dest: Where to copy to 523 | * @src: Where to copy from 524 | * @count: The size of the area. 525 | * 526 | * You should not use this function to access IO space, use memcpy_toio() 527 | * or memcpy_fromio() instead. 528 | */ 529 | void *memcpy(void *dest, const void *src, size_t count) 530 | { 531 | char *tmp = dest; 532 | const char *s = src; 533 | 534 | while (count--) 535 | *tmp++ = *s++; 536 | return dest; 537 | } 538 | #endif 539 | 540 | #ifndef __HAVE_ARCH_MEMMOVE 541 | /** 542 | * memmove - Copy one area of memory to another 543 | * @dest: Where to copy to 544 | * @src: Where to copy from 545 | * @count: The size of the area. 546 | * 547 | * Unlike memcpy(), memmove() copes with overlapping areas. 548 | */ 549 | void *memmove(void *dest, const void *src, size_t count) 550 | { 551 | char *tmp; 552 | const char *s; 553 | 554 | if (dest <= src) { 555 | tmp = dest; 556 | s = src; 557 | while (count--) 558 | *tmp++ = *s++; 559 | } else { 560 | tmp = dest; 561 | tmp += count; 562 | s = src; 563 | s += count; 564 | while (count--) 565 | *--tmp = *--s; 566 | } 567 | return dest; 568 | } 569 | #endif 570 | 571 | #ifndef __HAVE_ARCH_MEMCMP 572 | /** 573 | * memcmp - Compare two areas of memory 574 | * @cs: One area of memory 575 | * @ct: Another area of memory 576 | * @count: The size of the area. 577 | */ 578 | #undef memcmp 579 | int memcmp(const void *cs, const void *ct, size_t count) 580 | { 581 | const unsigned char *su1, *su2; 582 | int res = 0; 583 | 584 | for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) 585 | if ((res = *su1 - *su2) != 0) 586 | break; 587 | return res; 588 | } 589 | #endif 590 | 591 | #ifndef __HAVE_ARCH_MEMSCAN 592 | /** 593 | * memscan - Find a character in an area of memory. 594 | * @addr: The memory area 595 | * @c: The byte to search for 596 | * @size: The size of the area. 597 | * 598 | * returns the address of the first occurrence of @c, or 1 byte past 599 | * the area if @c is not found 600 | */ 601 | void *memscan(void *addr, int c, size_t size) 602 | { 603 | unsigned char *p = addr; 604 | 605 | while (size) { 606 | if (*p == c) 607 | return (void *)p; 608 | p++; 609 | size--; 610 | } 611 | return (void *)p; 612 | } 613 | #endif 614 | 615 | #ifndef __HAVE_ARCH_STRSTR 616 | /** 617 | * strstr - Find the first substring in a %NUL terminated string 618 | * @s1: The string to be searched 619 | * @s2: The string to search for 620 | */ 621 | char *strstr(const char *s1, const char *s2) 622 | { 623 | size_t l1, l2; 624 | 625 | l2 = strlen(s2); 626 | if (!l2) 627 | return (char *)s1; 628 | l1 = strlen(s1); 629 | while (l1 >= l2) { 630 | l1--; 631 | if (!memcmp(s1, s2, l2)) 632 | return (char *)s1; 633 | s1++; 634 | } 635 | return NULL; 636 | } 637 | #endif 638 | 639 | #ifndef __HAVE_ARCH_STRNSTR 640 | /** 641 | * strnstr - Find the first substring in a length-limited string 642 | * @s1: The string to be searched 643 | * @s2: The string to search for 644 | * @len: the maximum number of characters to search 645 | */ 646 | char *strnstr(const char *s1, const char *s2, size_t len) 647 | { 648 | size_t l2; 649 | 650 | l2 = strlen(s2); 651 | if (!l2) 652 | return (char *)s1; 653 | while (len >= l2) { 654 | len--; 655 | if (!memcmp(s1, s2, l2)) 656 | return (char *)s1; 657 | s1++; 658 | } 659 | return NULL; 660 | } 661 | #endif 662 | 663 | #ifndef __HAVE_ARCH_MEMCHR 664 | /** 665 | * memchr - Find a character in an area of memory. 666 | * @s: The memory area 667 | * @c: The byte to search for 668 | * @n: The size of the area. 669 | * 670 | * returns the address of the first occurrence of @c, or %NULL 671 | * if @c is not found 672 | */ 673 | void *memchr(const void *s, int c, size_t n) 674 | { 675 | const unsigned char *p = s; 676 | while (n-- != 0) { 677 | if ((unsigned char)c == *p++) { 678 | return (void *)(p - 1); 679 | } 680 | } 681 | return NULL; 682 | } 683 | #endif 684 | 685 | const char hex_asc[] = "0123456789abcdef"; 686 | -------------------------------------------------------------------------------- /string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Andrei Warkentin 3 | * 4 | * This program is free software ; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | #ifndef STRING_H 10 | #define STRING_H 11 | 12 | int strnicmp(const char *s1, const char *s2, size_t len); 13 | int strcasecmp(const char *s1, const char *s2); 14 | int strncasecmp(const char *s1, const char *s2, size_t n); 15 | char *strcpy(char *dest, const char *src); 16 | char *strncpy(char *dest, const char *src, size_t count); 17 | size_t strlcpy(char *dest, const char *src, size_t size); 18 | char *strcat(char *dest, const char *src); 19 | char *strncat(char *dest, const char *src, size_t count); 20 | size_t strlcat(char *dest, const char *src, size_t count); 21 | int strcmp(const char *cs, const char *ct); 22 | int strncmp(const char *cs, const char *ct, size_t count); 23 | char *strchr(const char *s, int c); 24 | char *strrchr(const char *s, int c); 25 | char *strnchr(const char *s, size_t count, int c); 26 | char *skip_spaces(const char *str); 27 | char *strim(char *s); 28 | size_t strlen(const char *s); 29 | size_t strnlen(const char *s, size_t count); 30 | size_t strspn(const char *s, const char *accept); 31 | size_t strcspn(const char *s, const char *reject); 32 | char *strpbrk(const char *cs, const char *ct); 33 | char *strsep(char **s, const char *ct); 34 | void *memset(void *s, int c, size_t count); 35 | void *memcpy(void *dest, const void *src, size_t count); 36 | void *memmove(void *dest, const void *src, size_t count); 37 | int memcmp(const void *cs, const void *ct, size_t count); 38 | void *memscan(void *addr, int c, size_t size); 39 | char *strstr(const char *s1, const char *s2); 40 | char *strnstr(const char *s1, const char *s2, size_t len); 41 | void *memchr(const void *s, int c, size_t n); 42 | 43 | extern const char hex_asc[]; 44 | #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] 45 | #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] 46 | 47 | static inline char *pack_hex_byte(char *buf, uint8_t byte) 48 | { 49 | *buf++ = hex_asc_hi(byte); 50 | *buf++ = hex_asc_lo(byte); 51 | return buf; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /tegra.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tegra defs. 3 | * 4 | * Copyright (C) 2018 Andrei Warkentin 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 | */ 20 | 21 | #ifndef TEGRA_H 22 | #define TEGRA_H 23 | 24 | #define TEGRA_EHCI_BASE 0x7d000000 25 | 26 | #define TEGRA_PMC_BASE 0x7000e400 27 | #define TEGRA_PMC_CONFIG (TEGRA_PMC_BASE + 0) 28 | #define TEGRA_PMC_CONFIG_RESET (0x10) 29 | #define TEGRA_PMC_SCRATCH0 (TEGRA_PMC_BASE + 0x50) 30 | 31 | typedef enum { 32 | REBOOT_NORMAL = 0, 33 | REBOOT_RCM = (1 << 1), 34 | REBOOT_BOOTLOADER = (1 << 30), 35 | REBOOT_RECOVERY = (1 << 31) 36 | } reboot_type; 37 | 38 | static inline void 39 | tegra_reboot(reboot_type type) 40 | { 41 | OUT32(type, TEGRA_PMC_SCRATCH0); 42 | OUT32(IN32(TEGRA_PMC_CONFIG) | TEGRA_PMC_CONFIG_RESET, TEGRA_PMC_CONFIG); 43 | } 44 | 45 | #endif /* TEGRA_H */ 46 | -------------------------------------------------------------------------------- /usb_descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | /* 21 | * Copyright (C) 2008 The Android Open Source Project 22 | * All rights reserved. 23 | * 24 | * Redistribution and use in source and binary forms, with or without 25 | * modification, are permitted provided that the following conditions 26 | * are met: 27 | * * Redistributions of source code must retain the above copyright 28 | * notice, this list of conditions and the following disclaimer. 29 | * * Redistributions in binary form must reproduce the above copyright 30 | * notice, this list of conditions and the following disclaimer in 31 | * the documentation and/or other materials provided with the 32 | * distribution. 33 | * 34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 41 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 42 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 43 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 44 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45 | * SUCH DAMAGE. 46 | */ 47 | 48 | #include 49 | #include 50 | 51 | #ifndef USB_DESCRIPTOR_H 52 | #define USB_DESCRIPTOR_H 53 | 54 | static unsigned short manufacturer_string[] = { 55 | (USB_DESC_TYPE_STRING << 8) | (9 * 2), 56 | 's', 'h', 'i', 'e', 'l', 'd', 'T', 'V', 57 | }; 58 | 59 | static unsigned short product_string[] = { 60 | (USB_DESC_TYPE_STRING << 8) | (20 * 2), 61 | 's', 'h', 'i', 'e', 'l', 'd', 'T', 'V', ' ', 'l', 'o', 'a', 'd', 'e', 'r', ' ', '1', '.', '0', 62 | }; 63 | 64 | static unsigned short default_string[] = { 65 | (USB_DESC_TYPE_STRING << 8) | (8 * 2), 66 | 'd', 'e', 'f', 'a', 'u', 'l', 't', 67 | }; 68 | 69 | static unsigned short language_table[] = { 70 | (USB_DESC_TYPE_STRING << 8) | 4, 71 | 0x0409, // LANGID for US English 72 | }; 73 | 74 | static unsigned char device_desc[] = { 75 | 18, // length 76 | USB_DESC_TYPE_DEVICE, // type 77 | 0x10, 0x02, // usb spec rev 1.00 78 | 0x00, // class 79 | 0x00, // subclass 80 | 0x00, // protocol 81 | 0x40, // max packet size 82 | 0xD1, 0x18, // vendor id 83 | 0x0D, 0xD0, // product id 84 | 0x00, 0x01, // version 1.0 85 | 0x01, // manufacturer str idx 86 | 0x02, // product str idx 87 | 0x00, // serial number index 88 | 0x01, // number of configs, 89 | }; 90 | 91 | static unsigned char config_desc[] = { 92 | 0x09, // length 93 | USB_DESC_TYPE_CONFIGURATION, 94 | 0x20, 0x00, // total length 95 | 0x01, // # interfaces 96 | 0x01, // config value 97 | 0x00, // config string 98 | 0x80, // attributes 99 | 0x80, // XXX max power (250ma) 100 | 101 | 0x09, // length 102 | USB_DESC_TYPE_INTERFACE, 103 | 0x00, // interface number 104 | 0x00, // alt number 105 | 0x02, // # endpoints 106 | 0xFF, 107 | 0x42, 108 | 0x03, 109 | 0x00, // interface string 110 | 111 | 0x07, // length 112 | USB_DESC_TYPE_ENDPOINT, 113 | 0x81, // in, #1 114 | 0x02, // bulk 115 | 0x00, 0x02, // max packet 512 116 | 0x00, // interval 117 | 118 | 0x07, // length 119 | USB_DESC_TYPE_ENDPOINT, 120 | 0x01, // out, #1 121 | 0x02, // bulk 122 | 0x00, 0x02, // max packet 512 123 | 0x01, // interval 124 | }; 125 | 126 | static unsigned char config_desc_fs[] = { 127 | 0x09, // length 128 | USB_DESC_TYPE_CONFIGURATION, 129 | 0x20, 0x00, // total length 130 | 0x01, // # interfaces 131 | 0x01, // config value 132 | 0x00, // config string 133 | 0x80, // attributes 134 | 0x80, // XXX max power (250ma) 135 | 136 | 0x09, // length 137 | USB_DESC_TYPE_INTERFACE, 138 | 0x00, // interface number 139 | 0x00, // alt number 140 | 0x02, // # endpoints 141 | 0xFF, 142 | 0x42, 143 | 0x03, 144 | 0x00, // interface string 145 | 146 | 0x07, // length 147 | USB_DESC_TYPE_ENDPOINT, 148 | 0x81, // in, #1 149 | 0x02, // bulk 150 | 0x40, 0x00, // max packet 64 151 | 0x00, // interval 152 | 153 | 0x07, // length 154 | USB_DESC_TYPE_ENDPOINT, 155 | 0x01, // out, #1 156 | 0x02, // bulk 157 | 0x40, 0x00, // max packet 64 158 | 0x00, // interval 159 | }; 160 | 161 | static usbd_desc_table descr_hs[] = { 162 | { device_desc, sizeof(device_desc), USB_DESC_ID(USB_DESC_TYPE_DEVICE, 0) }, 163 | { config_desc, sizeof(config_desc), USB_DESC_ID(USB_DESC_TYPE_CONFIGURATION, 0) }, 164 | { manufacturer_string, sizeof(manufacturer_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 1) }, 165 | { product_string, sizeof(product_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 2) }, 166 | { default_string, sizeof(default_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 4) }, 167 | { language_table, sizeof(language_table), USB_DESC_ID(USB_DESC_TYPE_STRING, 0) }, 168 | { 0, 0, 0 }, 169 | }; 170 | 171 | static usbd_desc_table descr_fs[] = { 172 | { device_desc, sizeof(device_desc), USB_DESC_ID(USB_DESC_TYPE_DEVICE, 0) }, 173 | { config_desc_fs, sizeof(config_desc), USB_DESC_ID(USB_DESC_TYPE_CONFIGURATION, 0) }, 174 | { manufacturer_string, sizeof(manufacturer_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 1) }, 175 | { product_string, sizeof(product_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 2) }, 176 | { default_string, sizeof(default_string), USB_DESC_ID(USB_DESC_TYPE_STRING, 4) }, 177 | { language_table, sizeof(language_table), USB_DESC_ID(USB_DESC_TYPE_STRING, 0) }, 178 | { 0, 0, 0 }, 179 | }; 180 | 181 | #endif /* USB_DESCRIPTOR_H */ 182 | -------------------------------------------------------------------------------- /usbd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | /* 21 | * Copyright (c) 2008, Google Inc. 22 | * All rights reserved. 23 | * 24 | * Redistribution and use in source and binary forms, with or without 25 | * modification, are permitted provided that the following conditions 26 | * are met: 27 | * * Redistributions of source code must retain the above copyright 28 | * notice, this list of conditions and the following disclaimer. 29 | * * Redistributions in binary form must reproduce the above copyright 30 | * notice, this list of conditions and the following disclaimer in 31 | * the documentation and/or other materials provided with the 32 | * distribution. 33 | * 34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 41 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 42 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 43 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 44 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45 | * SUCH DAMAGE. 46 | */ 47 | 48 | #include 49 | #include 50 | 51 | #define MAX_DMA_ADDR 0xffffffff 52 | #define EHCI_BASE (context->ehci_udc_base) 53 | 54 | #define QH_OFFSET_OUT(n) (EHCI_BASE + 0x1000 + (UN(n) * 0x80)) 55 | #define QH_OFFSET_IN(n) (EHCI_BASE + 0x1000 + (UN(n) * 0x80) + 0x40) 56 | #define USBMODE (EHCI_BASE + 0x1f8) 57 | #define USBLISTADR (EHCI_BASE + 0x148) 58 | #define USBCMD (EHCI_BASE + 0x130) 59 | #define USBSTS (EHCI_BASE + 0x134) 60 | #define USBINTR (EHCI_BASE + 0x138) 61 | #define USBDEVADDR (EHCI_BASE + 0x144) 62 | #define USBDEVLC (EHCI_BASE + 0x1b4) 63 | #define EPTSETUPST (EHCI_BASE + 0x208) 64 | #define EPTPRIME (EHCI_BASE + 0x20c) 65 | #define EPTFLUSH (EHCI_BASE + 0x210) 66 | #define EPTREADY (EHCI_BASE + 0x214) 67 | #define EPTCOMPLETE (EHCI_BASE + 0x218) 68 | #define EP_CTRL(n) (EHCI_BASE + 0x21c + (UN(n) * 4)) 69 | 70 | #define USBCMD_ITC_DEFAULT (8 << 16) 71 | #define USBCMD_SETUP_TRIPW BIT(13) 72 | #define USBCMD_RESET BIT(1) 73 | #define USBCMD_RUN BIT(0) 74 | #define USBSTS_SLI BIT(8) 75 | #define USBSTS_RESET BIT(6) 76 | #define USBSTS_PORT_CHANGE BIT(2) 77 | #define USBSTS_ERROR BIT(1) 78 | #define USBDEVLC_MODE(x) X(x, 25, 26) 79 | #define USBDEVLC_MODE_FULL (0) 80 | #define USBDEVLC_MODE_LOW (1) 81 | #define USBDEVLC_MODE_HIGH (2) 82 | #define USBMODE_DEVICE (2) 83 | #define USBMODE_MASK (3) 84 | #define USBDEVADDR_ADVANCE BIT(24) 85 | #define USBDEVADDR_SHIFT (25) 86 | #define EP_CTRL_RXS BIT(0) 87 | #define EP_CTRL_RXR BIT(6) 88 | #define EP_CTRL_RXE BIT(7) 89 | #define EP_CTRL_TXS BIT(16) 90 | #define EP_CTRL_TXR BIT(22) 91 | #define EP_CTRL_TXE BIT(23) 92 | 93 | #define MAX_EPS 16 94 | #define MAX_REQS (MAX_EPS * 2) 95 | static usbd_req *usbd_reqs[MAX_REQS]; 96 | 97 | static const char * const usbd_ep_type_names[] = { 98 | "EP_TYPE_NONE", 99 | "EP_TYPE_CTLR", 100 | "EP_TYPE_ISO", 101 | "EP_TYPE_BULK", 102 | "EP_TYPE_INTR" 103 | }; 104 | 105 | static void 106 | usbd_hw_ep_flush(usbd *context, 107 | int ep, 108 | bool_t in) 109 | { 110 | uint32_t bits = 0; 111 | 112 | if (ep == -1) { 113 | bits = 0xffffffff; 114 | } else if (ep == 0) { 115 | bits = BIT(16) | BIT(0); 116 | } else if (in) { 117 | bits = BIT(16 + ep); 118 | } else { 119 | bits = BIT(ep); 120 | } 121 | 122 | do { 123 | OUT32(bits, EPTFLUSH); 124 | } while (IN32(EPTREADY) & bits); 125 | } 126 | 127 | void 128 | usbd_req_init(usbd_req *req, 129 | usbd_ep *ep) 130 | { 131 | memset(req, 0, sizeof(*req)); 132 | req->ep = ep; 133 | 134 | req->buffer = req->small_buffer; 135 | BUG_ON (UN(req->buffer) > MAX_DMA_ADDR); 136 | } 137 | 138 | static usbd_ep * 139 | usbd_get_ep(usbd *context, 140 | int ep, 141 | bool_t send) 142 | { 143 | unsigned i; 144 | struct usbd_ep **pe = context->eps; 145 | 146 | if (ep == 0) { 147 | if (send) { 148 | return &context->ep0_in; 149 | } 150 | 151 | return &context->ep0_out; 152 | } 153 | 154 | if (pe == NULL) { 155 | return NULL; 156 | } 157 | 158 | for (i = 0; pe[i] != NULL; i++) { 159 | if (pe[i]->num != ep || 160 | pe[i]->send != send) { 161 | continue; 162 | } 163 | return pe[i]; 164 | } 165 | 166 | return NULL; 167 | } 168 | 169 | usbd_status 170 | usbd_init(usbd *context, 171 | usbd_td *qtds, 172 | size_t qtd_count) 173 | 174 | { 175 | size_t i; 176 | 177 | for (i = 0; i < qtd_count; i++) { 178 | BUG_ON ((UN(qtds + i) & (USBD_TD_ALIGNMENT - 1)) != 0); 179 | BUG_ON (UN(qtds + i) > MAX_DMA_ADDR); 180 | } 181 | 182 | context->qtds = qtds; 183 | context->qtd_count = qtd_count; 184 | 185 | context->ep0_out.num = 0; 186 | context->ep0_out.send = false; 187 | context->ep0_out.type = EP_TYPE_CTLR; 188 | 189 | context->ep0_in.num = 0; 190 | context->ep0_in.send = true; 191 | context->ep0_in.type = EP_TYPE_CTLR; 192 | 193 | for (i = 0; i < MAX_REQS; i++) { 194 | usbd_ep *ep; 195 | int num = i / 2; 196 | bool_t in = (i & 1) != 0; 197 | 198 | ep = usbd_get_ep(context, num, in); 199 | if (ep != NULL) { 200 | BUG_ON (num != ep->num); 201 | BUG_ON (ep->send != in); 202 | BUG_ON (qtd_count == 0); 203 | ep->qtd = qtds++; 204 | qtd_count--; 205 | } 206 | } 207 | 208 | usbd_req_init(&context->ep0_out_req, &context->ep0_out); 209 | usbd_req_init(&context->ep0_in_req, &context->ep0_in); 210 | 211 | context->hs = false; 212 | context->descs = NULL; 213 | context->current_config = 0; 214 | if (context->set_config != NULL) { 215 | context->set_config(context, context->current_config); 216 | } 217 | 218 | OUT32(USBCMD_ITC_DEFAULT | USBCMD_RESET, USBCMD); 219 | while((IN32(USBCMD) & USBCMD_RESET) != 0); 220 | 221 | OUT32(USBMODE_DEVICE, USBMODE); 222 | while((IN32(USBMODE) & USBMODE_MASK) != USBMODE_DEVICE); 223 | 224 | usbd_hw_ep_flush(context, -1, false); 225 | 226 | OUT32(QH_OFFSET_OUT(0), USBLISTADR); 227 | OUT32(USBCMD_ITC_DEFAULT | USBCMD_RUN, USBCMD); 228 | 229 | return USBD_SUCCESS; 230 | } 231 | 232 | void 233 | usbd_fini(usbd *context) 234 | { 235 | OUT32(USBCMD_ITC_DEFAULT | USBCMD_RESET, USBCMD); 236 | while((IN32(USBCMD) & USBCMD_RESET) != 0); 237 | } 238 | 239 | static void 240 | usbd_hw_ep_init(usbd *context, 241 | int ep, 242 | usbd_ep_type rx_type, 243 | usbd_ep_type tx_type) 244 | { 245 | uint32_t bits = 0; 246 | 247 | if (ep == 0) { 248 | /* Always on and CTLR. */ 249 | return; 250 | } 251 | 252 | if (rx_type != EP_TYPE_NONE) { 253 | rx_type--; 254 | bits |= I(rx_type, 3, 2) | EP_CTRL_RXE | EP_CTRL_RXR; 255 | } 256 | 257 | if (tx_type != EP_TYPE_NONE) { 258 | tx_type--; 259 | bits |= I(tx_type, 19, 18) | EP_CTRL_TXE | EP_CTRL_TXR; 260 | } 261 | 262 | OUT32(bits, EP_CTRL(ep)); 263 | } 264 | 265 | static uint32_t 266 | usbd_ep_get_max_packet(usbd *context, 267 | usbd_ep *e) 268 | { 269 | if (e->type == EP_TYPE_CTLR) { 270 | return USBD_CONTROL_MAX; 271 | } else if (e->type == EP_TYPE_BULK) { 272 | return context->hs ? USBD_HS_BULK_MAX : USBD_FS_BULK_MAX; 273 | } else if (e->type == EP_TYPE_INTR) { 274 | return context->hs ? USBD_HS_INTR_MAX : USBD_FS_INTR_MAX; 275 | } 276 | 277 | return context->hs ? USBD_HS_ISO_MAX : USBD_FS_ISO_MAX; 278 | } 279 | 280 | void 281 | usbd_ep_enable(usbd *context, 282 | usbd_ep *ep_out, 283 | usbd_ep *ep_in) 284 | { 285 | usbd_hw_ep_init(context, ep_out->num, ep_out->type, ep_in->type); 286 | } 287 | 288 | void 289 | usbd_ep_disable(usbd *context, 290 | usbd_ep *ep_out, 291 | usbd_ep *ep_in) 292 | { 293 | BUG_ON (ep_out->num != ep_in->num); 294 | usbd_hw_ep_init(context, ep_out->num, EP_TYPE_NONE, EP_TYPE_NONE); 295 | } 296 | 297 | static void 298 | usbd_hw_ep_stall(usbd *context, 299 | int ep) 300 | { 301 | OUT32(EP_CTRL_RXS | EP_CTRL_TXS, EP_CTRL(ep)); 302 | } 303 | 304 | static usbd_status 305 | usbd_port_change(usbd *context) 306 | { 307 | int mode; 308 | 309 | mode = USBDEVLC_MODE(IN32(USBDEVLC)); 310 | switch (mode) { 311 | case USBDEVLC_MODE_FULL: 312 | context->hs = false; 313 | context->descs = context->fs_descs; 314 | break; 315 | case USBDEVLC_MODE_HIGH: 316 | context->hs = true; 317 | context->descs = context->hs_descs; 318 | break; 319 | default: 320 | return USBD_PORT_CHANGE_ERROR; 321 | } 322 | 323 | return USBD_SUCCESS; 324 | } 325 | 326 | static usbd_status 327 | usbd_port_reset(usbd *context) 328 | { 329 | int i; 330 | 331 | OUT32(IN32(EPTCOMPLETE), EPTCOMPLETE); 332 | OUT32(IN32(EPTSETUPST), EPTSETUPST); 333 | usbd_hw_ep_flush(context, -1, false); 334 | 335 | for (i = 0; i < ELES(usbd_reqs); i++) { 336 | usbd_req *req = usbd_reqs[i]; 337 | 338 | if (req != NULL) { 339 | usbd_reqs[i] = NULL; 340 | req->error = true; 341 | if (req->complete != NULL) { 342 | req->complete(context, req); 343 | } 344 | } 345 | } 346 | 347 | for (i = 0; i < MAX_EPS; i++) { 348 | usbd_hw_ep_init(context, i, 349 | EP_TYPE_NONE, 350 | EP_TYPE_NONE); 351 | } 352 | 353 | if (context->port_reset == NULL) { 354 | return USBD_SUCCESS; 355 | } 356 | return context->port_reset(context); 357 | } 358 | 359 | static void 360 | usbd_qh_init(usbd *context, 361 | usbd_ep *ep, 362 | usbd_qh *qh) 363 | { 364 | uint32_t packet_len = usbd_ep_get_max_packet(context, ep); 365 | BUG_ON (packet_len == 0); 366 | 367 | memset(qh, 0, sizeof(*qh)); 368 | qh->max_pkt_length = 369 | (packet_len << USBD_QH_MAX_PKT_LEN_POS) | 370 | USBD_QH_ZLT_SEL; 371 | 372 | if (!ep->send && ep->num == 0) { 373 | qh->max_pkt_length |= USBD_QH_IOS; 374 | } 375 | 376 | qh->curr_dtd_ptr = USBD_TD_ADDR_MASK | USBD_TD_NEXT_TERMINATE; 377 | qh->next_dtd_ptr = (uint32_t) UN(ep->qtd); 378 | } 379 | 380 | static void 381 | usbd_td_init(usbd_td *td, 382 | uint32_t size, 383 | void *buf) 384 | { 385 | memset(td, 0, sizeof(*td)); 386 | td->next_td_ptr = USBD_TD_ADDR_MASK | USBD_TD_NEXT_TERMINATE; 387 | td->size_ioc_sts = (size << 16) | USBD_TD_STATUS_ACTIVE; 388 | td->buff_ptr0 = (uint32_t) UN(buf); 389 | if (size <= 0x1000) { 390 | return; 391 | } 392 | td->buff_ptr1 = (td->buff_ptr0 & 0xfffff000) + 0x1000; 393 | if (size <= 0x2000) { 394 | return; 395 | } 396 | td->buff_ptr2 = td->buff_ptr1 + 0x1000; 397 | if (size <= 0x3000) { 398 | return; 399 | } 400 | td->buff_ptr3 = td->buff_ptr2 + 0x1000; 401 | if (size <= 0x4000) { 402 | return; 403 | } 404 | td->buff_ptr4 = td->buff_ptr3 + 0x1000; 405 | } 406 | 407 | static void 408 | usbd_ep_prime(usbd *context, 409 | usbd_ep *ep) 410 | { 411 | uint32_t bits = 0; 412 | 413 | if (ep->send) { 414 | bits = BIT(16 + ep->num); 415 | } else { 416 | bits = BIT(ep->num); 417 | } 418 | 419 | OUT32(bits, EPTPRIME); 420 | } 421 | 422 | static void 423 | usbd_req_complete(usbd *context, 424 | usbd_req *req) 425 | { 426 | usbd_ep *ep = req->ep; 427 | 428 | BUG_ON(ep == NULL); 429 | 430 | if (req->complete == NULL) { 431 | return; 432 | } 433 | 434 | req->io_done = req->buffer_length - 435 | ((ep->qtd->size_ioc_sts & USBD_TD_PACKET_SIZE) >> 436 | USBD_TD_LENGTH_BIT_POS); 437 | req->error = (ep->qtd->size_ioc_sts & USBD_TD_ERROR_MASK) != 0; 438 | req->complete(context, req); 439 | } 440 | 441 | void 442 | usbd_req_cancel(usbd *context, 443 | usbd_req *req) 444 | { 445 | int ix; 446 | usbd_ep *ep = req->ep; 447 | 448 | BUG_ON(ep == NULL); 449 | ix = ep->num; 450 | 451 | if (ep->send) { 452 | ix += MAX_EPS; 453 | } 454 | 455 | if (usbd_reqs[ix] != req) { 456 | return; 457 | } 458 | 459 | usbd_hw_ep_flush(context, ep->num, ep->send); 460 | 461 | usbd_reqs[ix] = NULL; 462 | req->error = true; 463 | req->cancel = true; 464 | if (req->complete != NULL) { 465 | req->complete(context, req); 466 | } 467 | req->cancel = false; 468 | } 469 | 470 | usbd_status 471 | usbd_req_submit(usbd *context, 472 | usbd_req *req) 473 | { 474 | int ix; 475 | usbd_qh *qh; 476 | usbd_ep *ep = req->ep; 477 | 478 | BUG_ON(ep == NULL); 479 | ix = ep->num; 480 | 481 | req->buffer_length = min(req->buffer_length, 482 | (uint32_t) 0x5000); 483 | 484 | if (ep->send) { 485 | qh = (usbd_qh *) QH_OFFSET_IN(ep->num); 486 | ix += MAX_EPS; 487 | } else { 488 | qh = (usbd_qh *) QH_OFFSET_OUT(ep->num); 489 | } 490 | 491 | BUG_ON_EX(usbd_reqs[ix] != NULL, "already in flight for EP%u %s", 492 | ep->num, ep->send ? "in" : "out"); 493 | 494 | usbd_hw_ep_flush(context, ep->num, ep->send); 495 | 496 | if (req->buffer_length != 0) { 497 | if (ep->send) { 498 | DSB_ST(); 499 | } else { 500 | DSB_LD(); 501 | } 502 | } 503 | 504 | usbd_td_init(ep->qtd, req->buffer_length, req->buffer); 505 | DSB_ST(); 506 | 507 | usbd_qh_init(context, ep, qh); 508 | DSB_ST(); 509 | 510 | usbd_ep_prime(context, ep); 511 | usbd_reqs[ix] = req; 512 | 513 | return USBD_SUCCESS; 514 | } 515 | 516 | static void 517 | usbd_ep0_setup_ack(usbd *context) 518 | { 519 | context->ep0_in_req.buffer_length = 0; 520 | context->ep0_in_req.complete = NULL; 521 | 522 | usbd_req_submit(context, &context->ep0_in_req); 523 | } 524 | 525 | static void 526 | usbd_ep0_in_req_complete(usbd *context, 527 | usbd_req *req) 528 | { 529 | if (req->error) { 530 | return; 531 | } 532 | 533 | context->ep0_out_req.buffer_length = 0; 534 | context->ep0_out_req.complete = NULL; 535 | usbd_req_submit(context, &context->ep0_out_req); 536 | } 537 | 538 | static void 539 | usbd_ep0_setup_tx(usbd *context, 540 | void *buf, 541 | uint32_t buffer_length) 542 | { 543 | context->ep0_in_req.buffer_length = buffer_length; 544 | memcpy(context->ep0_in_req.buffer, buf, buffer_length); 545 | context->ep0_in_req.complete = usbd_ep0_in_req_complete;; 546 | 547 | usbd_req_submit(context, &context->ep0_in_req); 548 | } 549 | 550 | static usbd_status 551 | usbd_ep0_setup(usbd *context, 552 | usb_ctrlrequest *request) 553 | { 554 | usbd_desc_table *d; 555 | usbd_status status = USBD_SETUP_PACKET_UNSUPPORTED; 556 | 557 | #define ST(bRT, bR) (((bRT) << 8) | (bR)) 558 | switch (ST(request->bRequestType, 559 | request->bRequest)) { 560 | case ST(USB_REQ_DEV_W, USB_REQ_SET_ADDRESS): 561 | OUT32(USBDEVADDR_ADVANCE | 562 | (request->wValue << USBDEVADDR_SHIFT), 563 | USBDEVADDR); 564 | usbd_ep0_setup_ack(context); 565 | return USBD_SUCCESS; 566 | case ST(USB_REQ_DEV_R, USB_REQ_GET_DESCRIPTOR): 567 | if (context->descs == NULL) { 568 | break; 569 | } 570 | d = context->descs; 571 | 572 | while (d->data) { 573 | if (request->wValue != d->id) { 574 | d++; 575 | continue; 576 | } 577 | 578 | usbd_ep0_setup_tx(context, d->data, 579 | min(d->length, request->wLength)); 580 | return USBD_SUCCESS; 581 | } 582 | 583 | break; 584 | case ST(USB_REQ_DEV_W, USB_REQ_SET_CONFIGURATION): 585 | if (request->wValue != context->current_config) { 586 | if (context->set_config != NULL) { 587 | /* 588 | * Could return USBD_CONFIG_UNSUPPORTED. 589 | */ 590 | status = context->set_config(context, 591 | (uint8_t) request->wValue); 592 | if (status != USBD_SUCCESS) { 593 | break; 594 | } 595 | } 596 | } 597 | 598 | context->current_config = request->wValue; 599 | usbd_ep0_setup_ack(context); 600 | return USBD_SUCCESS; 601 | break; 602 | case ST(USB_REQ_DEV_R, USB_REQ_GET_CONFIGURATION): 603 | usbd_ep0_setup_tx(context, &context->current_config, 604 | sizeof(context->current_config)); 605 | return USBD_SUCCESS; 606 | } 607 | #undef ST 608 | 609 | return status; 610 | } 611 | 612 | static usbd_status 613 | usbd_port_setup(usbd *context, 614 | uint32_t setupst) 615 | { 616 | int ep_ix; 617 | usbd_qh *qh; 618 | usbd_status setup_status; 619 | 620 | for (ep_ix = 0; setupst; setupst >>= 1, ep_ix++) { 621 | usb_ctrlrequest request; 622 | 623 | if ((setupst & 1) != 1) { 624 | continue; 625 | } 626 | 627 | qh = (usbd_qh *) QH_OFFSET_OUT(ep_ix); 628 | memcpy(&request, qh->setup_buffer, 629 | sizeof(usb_ctrlrequest)); 630 | 631 | OUT32(BIT(ep_ix), EPTSETUPST); 632 | while ((IN32(EPTSETUPST) & BIT(ep_ix)) != 0); 633 | 634 | setup_status = USBD_SETUP_PACKET_UNSUPPORTED; 635 | if (ep_ix == 0) { 636 | setup_status = usbd_ep0_setup(context, &request); 637 | } else if (context->port_setup != 0) { 638 | setup_status = context->port_setup(context, 639 | ep_ix, &request); 640 | } 641 | 642 | if (setup_status != USBD_SUCCESS) { 643 | #ifdef DEBUG 644 | printk("Stalling bRT %u bR %u wV 0x%x wI 0x%x wL 0x%x\n", 645 | request.bRequestType, 646 | request.bRequest, 647 | request.wValue, 648 | request.wIndex, 649 | request.wLength); 650 | #endif /* DEBUG */ 651 | usbd_hw_ep_stall(context, ep_ix); 652 | } 653 | } 654 | 655 | return USBD_SUCCESS; 656 | } 657 | 658 | static void 659 | usbd_completions(usbd *context, 660 | uint32_t complete) 661 | { 662 | int ep_ix; 663 | 664 | for (ep_ix = 0; complete; complete >>= 1, ep_ix++) { 665 | usbd_req *ep = usbd_reqs[ep_ix]; 666 | 667 | if ((complete & 1) == 0) { 668 | continue; 669 | } 670 | 671 | usbd_reqs[ep_ix] = NULL; 672 | DSB_LD(); 673 | usbd_req_complete(context, ep); 674 | } 675 | } 676 | 677 | usbd_status 678 | usbd_poll(usbd *context) 679 | { 680 | uint32_t status; 681 | uint32_t setupst; 682 | uint32_t complete; 683 | 684 | status = IN32(USBSTS); 685 | if (status != 0) { 686 | OUT32(status, USBSTS); 687 | 688 | if ((status & USBSTS_SLI) != 0) { 689 | return usbd_init(context, context->qtds, 690 | context->qtd_count); 691 | } else if ((status & USBSTS_RESET) != 0) { 692 | return usbd_port_reset(context); 693 | } else if ((status && USBSTS_PORT_CHANGE) != 0) { 694 | return usbd_port_change(context); 695 | } else { 696 | printk("USB error 0x%x\n", status); 697 | return USBD_USBSTS_ERROR; 698 | } 699 | } 700 | 701 | complete = IN32(EPTCOMPLETE); 702 | if (complete != 0) { 703 | OUT32(complete, EPTCOMPLETE); 704 | usbd_completions(context, complete); 705 | } 706 | 707 | setupst = IN32(EPTSETUPST); 708 | if (setupst != 0) { 709 | return usbd_port_setup(context, setupst); 710 | } 711 | 712 | return USBD_SUCCESS; 713 | } 714 | -------------------------------------------------------------------------------- /usbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Andrei Warkentin 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License as 6 | * published by the Free Software Foundation; either version 2 of 7 | * the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17 | * MA 02111-1307 USA 18 | */ 19 | 20 | /* 21 | * Copyright (c) 2008, Google Inc. 22 | * All rights reserved. 23 | * 24 | * Redistribution and use in source and binary forms, with or without 25 | * modification, are permitted provided that the following conditions 26 | * are met: 27 | * * Redistributions of source code must retain the above copyright 28 | * notice, this list of conditions and the following disclaimer. 29 | * * Redistributions in binary form must reproduce the above copyright 30 | * notice, this list of conditions and the following disclaimer in 31 | * the documentation and/or other materials provided with the 32 | * distribution. 33 | * 34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 41 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 42 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 43 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 44 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45 | * SUCH DAMAGE. 46 | */ 47 | 48 | #ifndef USBD_H 49 | #define USBD_H 50 | 51 | #include 52 | 53 | /* 54 | * Endpoint Transfer Descriptor is a 28 byte structure, aligned on a 32-byte 55 | * boundary. 56 | * 57 | */ 58 | typedef struct usbd_td { 59 | #define USBD_TD_NEXT_TERMINATE 0x00000001 60 | uint32_t next_td_ptr; /* Next TD pointer(31-5), T(0) set 61 | indicate invalid */ 62 | #define USBD_TD_IOC 0x00008000 63 | #define USBD_TD_STATUS_ACTIVE 0x00000080 64 | #define USBD_TD_STATUS_HALTED 0x00000040 65 | #define USBD_TD_STATUS_DATA_BUFF_ERR 0x00000020 66 | #define USBD_TD_STATUS_TRANSACTION_ERR 0x00000008 67 | #define USBD_TD_RESERVED_FIELDS 0x80007300 68 | uint32_t size_ioc_sts; /* Total bytes (30-16), IOC (15), 69 | MultO(11-10), STS (7-0) */ 70 | uint32_t buff_ptr0; /* Buffer pointer Page 0 */ 71 | uint32_t buff_ptr1; /* Buffer pointer Page 1 */ 72 | uint32_t buff_ptr2; /* Buffer pointer Page 2 */ 73 | uint32_t buff_ptr3; /* Buffer pointer Page 3 */ 74 | uint32_t buff_ptr4; /* Buffer pointer Page 4 */ 75 | uint32_t dummy_for_aligned_qtd_packing; 76 | } __packed usbd_td; 77 | 78 | #define USBD_TD_ADDR_MASK 0xFFFFFFE0 79 | #define USBD_TD_PACKET_SIZE 0x7FFF0000 80 | #define USBD_TD_LENGTH_BIT_POS 16 81 | #define USBD_TD_ERROR_MASK (USBD_TD_STATUS_HALTED | \ 82 | USBD_TD_STATUS_DATA_BUFF_ERR | \ 83 | USBD_TD_STATUS_TRANSACTION_ERR) 84 | #define USBD_TD_ALIGNMENT 0x20 85 | 86 | /* 87 | * Endpoint Queue Head. 88 | */ 89 | typedef struct usbd_qh { 90 | uint32_t max_pkt_length; /* Mult(31-30), Zlt(29), Max Pkt len and IOS(15) */ 91 | uint32_t curr_dtd_ptr; /* Current dTD Pointer(31-5) */ 92 | uint32_t next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ 93 | uint32_t size_ioc_int_sts; /* Total bytes (30-16), IOC (15), 94 | MultO(11-10), STS (7-0) */ 95 | uint32_t buff_ptr0; /* Buffer pointer Page 0 (31-12) */ 96 | uint32_t buff_ptr1; /* Buffer pointer Page 1 (31-12) */ 97 | uint32_t buff_ptr2; /* Buffer pointer Page 2 (31-12) */ 98 | uint32_t buff_ptr3; /* Buffer pointer Page 3 (31-12) */ 99 | uint32_t buff_ptr4; /* Buffer pointer Page 4 (31-12) */ 100 | uint32_t res1; 101 | uint8_t setup_buffer[8]; /* Setup data 8 bytes */ 102 | uint32_t res2[4]; 103 | } __packed usbd_qh; 104 | 105 | #define USBD_QH_MULT_POS 30 106 | #define USBD_QH_ZLT_SEL 0x20000000 107 | #define USBD_QH_MAX_PKT_LEN_POS 16 108 | #define USBD_QH_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) 109 | #define USBD_QH_IOS 0x00008000 110 | #define USBD_QH_NEXT_TERMINATE 0x00000001 111 | #define USBD_QH_IOC 0x00008000 112 | #define USBD_QH_MULTO 0x00000C00 113 | #define USBD_QH_STATUS_HALT 0x00000040 114 | #define USBD_QH_STATUS_ACTIVE 0x00000080 115 | #define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF 116 | #define USBD_QH_NEXT_POINTER_MASK 0xFFFFFFE0 117 | #define EP_QUEUE_FRINDEX_MASK 0x000007FF 118 | #define EP_MAX_LENGTH_TRANSFER 0x4000 119 | 120 | #define USBD_CONTROL_MAX 64 121 | #define USBD_FS_BULK_MAX 64 122 | #define USBD_HS_BULK_MAX 512 123 | #define USBD_FS_INTR_MAX 64 124 | #define USBD_HS_INTR_MAX 1024 125 | #define USBD_FS_ISO_MAX 1023 126 | #define USBD_HS_ISO_MAX 1024 127 | 128 | typedef struct usb_ctrlrequest { 129 | #define USB_REQ_DEV_R 0x80 130 | #define USB_REQ_DEV_W 0x00 131 | #define USB_REQ_IFACE_R 0x81 132 | #define USB_REQ_IFACE_W 0x01 133 | #define USB_REQ_EP_R 0x82 134 | #define USB_REQ_EP_W 0x02 135 | uint8_t bRequestType; 136 | #define USB_REQ_SET_ADDRESS 0x5 137 | #define USB_REQ_GET_DESCRIPTOR 0x6 138 | #define USB_REQ_GET_CONFIGURATION 0x8 139 | #define USB_REQ_SET_CONFIGURATION 0x9 140 | uint8_t bRequest; 141 | uint16_t wValue; 142 | uint16_t wIndex; 143 | uint16_t wLength; 144 | } __packed usb_ctrlrequest; 145 | 146 | #define USBD_ALIGNMENT USBD_TD_ALIGNMENT 147 | 148 | typedef enum { 149 | USBD_SUCCESS, 150 | USBD_PORT_CHANGE_ERROR, 151 | USBD_USBSTS_ERROR, 152 | USBD_SETUP_PACKET_UNSUPPORTED, 153 | USBD_CONFIG_UNSUPPORTED, 154 | } usbd_status; 155 | 156 | typedef struct { 157 | void *data; 158 | uint16_t length; 159 | uint16_t id; 160 | } usbd_desc_table; 161 | 162 | #define USB_DESC_ID(type,num) ((type << 8) | num) 163 | 164 | #define USB_DESC_TYPE_DEVICE 0x01 165 | #define USB_DESC_TYPE_CONFIGURATION 0x02 166 | #define USB_DESC_TYPE_STRING 0x03 167 | #define USB_DESC_TYPE_INTERFACE 0x04 168 | #define USB_DESC_TYPE_ENDPOINT 0x05 169 | #define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 170 | #define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07 171 | #define USB_DESC_TYPE_INTERFACE_POWER 0x08 172 | #define USB_DESC_TYPE_HID 0x21 173 | #define USB_DESC_TYPE_REPORT 0x22 174 | 175 | struct usbd; 176 | struct usbd_ep; 177 | 178 | typedef struct usbd_req { 179 | bool_t error; 180 | bool_t cancel; 181 | void *buffer; 182 | uint32_t io_done; 183 | uint32_t buffer_length; 184 | void (*complete)(struct usbd *context, struct usbd_req *req); 185 | /* 186 | * No user-servicable parts below, initialized by 187 | * usbd_req_init, which is called once during creation. 188 | */ 189 | uint8_t small_buffer[USBD_CONTROL_MAX]; 190 | struct usbd_ep *ep; 191 | } usbd_req; 192 | 193 | typedef enum usbd_ep_type { 194 | EP_TYPE_NONE, 195 | EP_TYPE_CTLR, 196 | EP_TYPE_ISO, 197 | EP_TYPE_BULK, 198 | EP_TYPE_INTR, 199 | } usbd_ep_type; 200 | 201 | typedef struct usbd_ep { 202 | int num; 203 | bool_t send; 204 | usbd_ep_type type; 205 | /* 206 | * No user-servicable parts below, initialized by 207 | * usbd_init. 208 | */ 209 | usbd_td *qtd; 210 | } usbd_ep; 211 | 212 | typedef struct usbd { 213 | void *ctx; 214 | phys_addr_t ehci_udc_base; 215 | /* 216 | * Everything but EP0 (which is already covered 217 | * by ep0_out/ep0_in). 218 | */ 219 | usbd_ep **eps; 220 | usbd_desc_table *fs_descs; 221 | usbd_desc_table *hs_descs; 222 | usbd_status (*port_reset)(struct usbd *); 223 | usbd_status (*port_setup)(struct usbd *, int ep, 224 | usb_ctrlrequest *req); 225 | usbd_status (*set_config)(struct usbd *, uint8_t config); 226 | /* 227 | * No user-servicable parts below, initialized by 228 | * usbd_init. 229 | */ 230 | usbd_td *qtds; 231 | size_t qtd_count; 232 | usbd_ep ep0_out; 233 | usbd_ep ep0_in; 234 | usbd_req ep0_out_req; 235 | usbd_req ep0_in_req; 236 | bool_t hs; 237 | usbd_desc_table *descs; 238 | uint8_t current_config; 239 | } usbd; 240 | 241 | usbd_status usbd_init(usbd *context, usbd_td *qtds,size_t qtd_count); 242 | void usbd_fini(usbd *context); 243 | usbd_status usbd_poll(usbd *context); 244 | void usbd_ep_enable(usbd *context, usbd_ep *ep_out, usbd_ep *ep_in); 245 | void usbd_ep_disable(usbd *context, usbd_ep *ep_out, usbd_ep *ep_in); 246 | void usbd_req_init(usbd_req *req, usbd_ep *ep); 247 | usbd_status usbd_req_submit(usbd *context, usbd_req *req); 248 | void usbd_req_cancel(usbd *context, usbd_req *req); 249 | 250 | #endif /* USBD_H */ 251 | -------------------------------------------------------------------------------- /video_fb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 1997-2002 ELTEC Elektronik AG 3 | * Frank Gottschling 4 | * 5 | * See file CREDITS for list of people who contributed to this 6 | * project. 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation; either version 2 of 11 | * the License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21 | * MA 02111-1307 USA 22 | */ 23 | 24 | #ifndef _VIDEO_FB_H_ 25 | #define _VIDEO_FB_H_ 26 | 27 | #define CONSOLE_BG_COL 0x00 28 | #define CONSOLE_FG_COL 0xa0 29 | 30 | /* 31 | * Graphic Data Format (GDF) bits for VIDEO_DATA_FORMAT 32 | */ 33 | #define GDF__8BIT_INDEX 0 34 | #define GDF_15BIT_555RGB 1 35 | #define GDF_16BIT_565RGB 2 36 | #define GDF_32BIT_X888RGB 3 37 | #define GDF_24BIT_888RGB 4 38 | #define GDF__8BIT_332RGB 5 39 | 40 | #define CONFIG_VIDEO_FB_LITTLE_ENDIAN 41 | #define CONFIG_VIDEO_VISIBLE_COLS 1920 42 | #define CONFIG_VIDEO_VISIBLE_ROWS 1080 43 | #define CONFIG_VIDEO_PIXEL_SIZE 4 44 | #define CONFIG_VIDEO_DATA_FORMAT GDF_32BIT_X888RGB /* BGR actually, but w/e */ 45 | 46 | int video_init(void *fb); 47 | void video_puts(const char *s); 48 | 49 | #endif /*_VIDEO_FB_H_ */ 50 | -------------------------------------------------------------------------------- /vsprintf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Andrei Warkentin 3 | * 4 | * This program is free software ; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as 6 | * published by the Free Software Foundation. 7 | */ 8 | 9 | #ifndef VSPRINTF_H 10 | #define VSPRINTF_H 11 | 12 | struct va_format { 13 | const char *fmt; 14 | va_list *va; 15 | }; 16 | 17 | unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); 18 | 19 | int scnprintf(char *buf, size_t size, const char *fmt, ...); 20 | int vscnprintf(char *buf, size_t size, const char *fmt, va_list args); 21 | int vsnprintf(char *buf, size_t size, const char *fmt, va_list args); 22 | 23 | #endif /* VSPRINTF_H */ 24 | --------------------------------------------------------------------------------