Related
I'm developing a homebrew os and I can't figure out how to switch from my homebrew bootloader to my homebrew kernel.
I do not know how it works
I know it starts with memory, but I don't know what number it starts with
I've been told that I can just look at the minix source, but when I parse it from the iso, it doesn't tell me anything at all, all it tells me is the configuration of the linux directory.
I've been trying to figure it out for a week, but nothing has come up.
I'm sorry if I'm not trying hard enough.
And since there didn't seem to be any similar questions, I thought I'd ask you a question
I'll show you a well commented minimal example of jumping to a C++ kernel I wrote so maybe you can go from there and write your own. I work on Linux Ubuntu 20.
In your home directory create a folder named OS and create a file named kernel.cpp in that folder. Place the following content in that file:
void map_framebuffer(){
unsigned long* page_directory_table_pointer = (unsigned long*) 0xb018;
*page_directory_table_pointer = 0x1e00f;
unsigned long* page_table_pointer = (unsigned long*) 0x1e000;
/*
This is the actual address of the framebuffer in QEMU
for the video mode I set up in boot.asm.
In this case it is hardcoded. You may want to actually get this from the VBE structure.
The VBE structure is placed at 0x7e00 in memory by the boot.asm routine.
*/
unsigned long framebuffer_address = (unsigned long) 0xfd00000f;
for (unsigned int i = 0; i < 512; i++){
*page_table_pointer = framebuffer_address;
framebuffer_address += 0x1000;
page_table_pointer++;
}
}
void main(){
map_framebuffer();
/*
I made sure that the framebuffer is mapped to adress 0x600000 in the map_framebuffer() function.
You may want to identity map it instead or map it somewhere else. You'll then have to
calculate page table offsets and stuff.
*/
unsigned char* framebuffer_pointer = (unsigned char*)0x600000;
//Make a few pixels become white. Each pixel is 3 bytes RGB.
for (unsigned int i = 0; i < 10000; i++)
*(framebuffer_pointer + i) = 0xff;
asm volatile(
"halt:\n\t"
"hlt\n\t"
"jmp halt\n\t");
}
/*
Memory map of kernel
7e00 - 8000 -> VBE INFO structure
8000 - 9000 -> paging.bin (booting asm code)
9000 - a000 -> pml4
a000 - b000 -> pdpt
b000 - c000 -> pdt
1b000 - 1c000 -> pt1 (kernel identity mapped pages)
1c000 - 1d000 -> pt2 (kernel identity mapped pages)
1d000 - 1e000 -> pt3 (kernel identity mapped pages)
1e000 - 1f000 -> pt4 (mapped to the framebuffer, only 2MB are mapped to the framebuffer which could take more than this)
30000 - 80000 -> kernel.elf (temporary kernel file)
80000 -> kernel stack (growing downward)
*/
Now create a file named boot.asm in the same directory and write the following content into it:
org 0x7c00
bits 16
;Set the video mode to VBE graphics mode for being able to draw onto the screen
jmp get_vbe_info
error:
mov ah, 0x0e
mov al, 'e'
int 0x10
hlt
get_vbe_info:
mov ax, 0x4f01 ;c0 00 00 00
mov cx, 0x118
xor bx, bx
mov es, bx
mov di, 0x7e00
int 0x10
cmp ax, 0x004f
jne error
set_video_mode:
mov ax, 0x4f02
mov bx, 0x4118
int 0x10
cmp ax, 0x004f
jne error
;Set up a real mode stack
mov ax, 0x00
mov ss, ax
mov sp, 0x7c00
;Load the second stage bootloader at 0x8000
mov ah, 0x00 ;reset drive
int 0x13
xor ax, ax
mov es, ax
mov bx, 0x8000 ;es:bx = 0x0000:0x8000 = 0x8000
mov ah, 0x02 ;read sector function
mov al, 0x09 ;read 9 sectors
mov ch, 0x00 ;cylinder 0
mov dh, 0x00 ;head 0
mov cl, 0x02 ;start from sector 2
int 0x13
;Load the cpp kernel at 0x30000
mov ah, 0x00
int 0x13
mov ax, 0x3000
mov es, ax
xor bx, bx ;es:bx = 0x3000:0x0000 = 0x3000 * 0x10 + 0x0 = 0x30000
mov ah, 0x02 ;read sector function
mov al, 0x3c ;read 60 sectors to load the whole file
mov ch, 0x00 ;cylinder 0
mov dh, 0x00 ;head 0
mov cl, 0x0a ;start from sector 10 (because this is where lies the ELF CPP kernel in the disk.img we created with dd)
int 0x13
xor ax, ax
mov ds, ax
;Set up a protected mode stack
mov ax, 0x10 ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov sp, 0x7c00
cli
lgdt[gdtr]
;Enable protected mode
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x08:protectedMode ;0x08 = 1000b, 1 for segment selector
;0 for gdt not ldt and 00 for privilege
bits 32
protectedMode:
mov ax, 0x10
mov ds, ax
;Jump to 0x8000 (the second stage bootloader)
jmp 0x08:0x8000
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF ;limit 0-15
dw 0x0 ;base 0-15
db 0x0 ;base 16-23
db 10011010b ;pr, privi (2), s, ex, dc, rw, ac
db 11001111b ;gr, sz, limit 16-19
db 0x0 ;base 24-31
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdtr:
dw 24
dd gdt_start
times 510 - ($-$$) db 0
dw 0xAA55
Now create a third file named paging.asm and place the following into it:
org 0x8000
bits 32
;Set up paging
mov eax, 0x00009008
mov cr3, eax
pml4t:
mov dword [0x9000], 0x0000a00f
mov dword [0x9004], 0x0
pdpt:
mov dword [0xa000], 0x0000b00f
mov dword [0xa004], 0x0
pdt:
mov dword [0xb000], 0x0001b00f
mov dword [0xb004], 0x0
mov dword [0xb008], 0x0001c00f
mov dword [0xb00c], 0x0
mov dword [0xb010], 0x0001d00f
mov dword [0xb014], 0x0
pt:
mov edx, 0x3
mov eax, 0x200
mov ebx, 0x0000000f
mov ecx, 0x1b000
next_table:
next_entry:
mov dword [ecx], ebx
add ecx, 0x4
mov dword [ecx], 0x0
add ebx, 0x1000 ;add 4096 to the adress pointed to by ebx (the next physical page)
add ecx, 0x4
sub eax, 0x1
cmp eax, 0x0
jne next_entry
mov eax, 0x200
sub edx, 0x1
cmp edx, 0x0
jne next_table
mov eax, cr4 ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080 ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0 ;enable paging
or eax, 1 << 31
mov cr0, eax
lgdt[gdtr] ;load a 64 bit gdt (will be ignored afterwards)
jmp 0x08:longMode
bits 64
longMode:
mov ax, 0x10 ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov rsp, 0x80000
mov rdi, 0x30000 ;address where elf reading occurs
mov rsi, 0x30000
add rdi, 24 ;program entry
mov rax, [rdi] ;placed in rax
add rdi, 8 ;program header table position
mov rbx, [rdi] ;put it in rbx
add rdi, 24 ;move to number of entries in program header
mov cx, [rdi] ;put it in cx (2 bytes)
mov rdi, 0x30000 ;beginning of file
add rdi, rbx ;go to program header 0
add rsi, rbx
next_segment:
mov edx, [rdi] ;put the type of segment in edx (4 bytes)
cmp edx, 0x1 ;determine if it is loadable
jne not_loadable ;if it isnt jump to not_loadable
add rdi, 0x8 ;else load it
mov r8, [rdi] ;put the offset in the file where data for this segment resides in r8
add rdi, 0x8 ;go to virtual address of segment
mov r9, [rdi] ;put it in r9
add rdi, 0x10 ;move to size of segment in file
mov r10, [rdi] ;put it in r10
add rdi, 0x8 ;move to size of segment in memory
mov r11, [rdi] ;put it in r11
mov rdi, 0x30000 ;move back to beginning of file
add rdi, r8 ;add segment offset to be at the segment position
next_byte:
mov dl, [rdi] ;put the byte at rdi in dl
mov [r9], dl ;move it to virtual address in r9
add r9, 0x1 ;add 1 byte to virtual address
add rdi, 0x1 ;add 1 byte to rdi
sub r10, 0x1 ;substract 1 byte from size of segment in file
sub r11, 0x1 ;substract 1 byte from size of segment in memory
cmp r10, 0x0 ;is segment finished
jne next_byte ;if not go to next_byte
cmp r11, 0x0 ;if yes tcheck memory size
je no_padding ;if no padding required jmp to no_padding
padding:
xor dl, dl
mov [r9], dl
add r9, 0x01
sub r11, 0x1
cmp r11, 0x0
jne padding
not_loadable:
no_padding:
sub cx, 0x1
cmp cx, 0x0
je finished
add rsi, 56
mov rdi, rsi
jmp next_segment
finished:
jmp rax ;Jump to the entry point of the ELF CPP file
halt:
hlt
jmp halt
;The 64-bits GDT
gdt_start:
dw 0xFFFF
dw 0
db 0
db 0
db 1
db 0
gdt_code:
dw 0x1111 ;limit 0-15
dw 0x0 ;base 0-15
db 0x0 ;base 16-23
db 10011010b ;pr, privi (2), s, ex, dc, rw, ac
db 10101111b ;gr, sz, limit 16-19
db 0x0 ;base 24-31
gdt_data:
dw 0x1111
dw 0x0
db 0x0
db 10010010b
db 00001111b
db 0x0
gdtr:
dw $ - gdt_start - 1
dq gdt_start
Now open a bash terminal and type the following commands in order (making sure to have NASM, g++ and QEMU installed):
nasm -fbin OS/boot.asm -oOS/boot.bin
nasm -fbin OS/paging.asm -oOS/paging.bin
g++ -static -ffreestanding -nostdlib -c -m64 OS/kernel.cpp -oOS/kernel.o
ld --entry main --oformat elf64-x86-64 --no-dynamic-linker -static -nostdlib OS/kernel.o -oOS/kernel.elf
dd if=/dev/zero of=OS/disk.img count=100 & dd if=OS/boot.bin of=OS/disk.img conv=notrunc & dd if=OS/paging.bin of=OS/disk.img seek=1 conv=notrunc & dd if=OS/kernel.elf of=OS/disk.img seek=9 conv=notrunc
qemu-system-x86_64 -hda OS/disk.img -s
You can always put all that in a bash file and launch it automatically. The -s in the QEMU command makes the virtual machine listen on port 1234. You can use gdb to debug the kernel. Type in gdb in a bash shell then target remote localhost:1234. You will then be debugging the machine. You can type dump memory result.bin 0x1000 0x2000 to dump the memory of the machine from 0x1000 to 0x2000 (or any address) in the file result.bin. You can use hexdump -C result.bin to look at the memory dump from terminal. The output is little endian so you need to reverse it for the actual values.
In the code above I'm loading the ELF file compiled with g++ to address 0x30000. Then I'm parsing the file and jumping to its entry point. The cpp code maps the framebuffer in virtual memory and makes a few pixels become white. The code is far from perfect but if you understand it in its entirety then you can go a long way.
I'm writing a UDP socket in nasm and I'm using the recvfrom system call to receive a UDP packet from a client.
I can successfully get the message sent, but I would like to send back to the client an answer.
The problem is that I can't extract the source IP address of the packet, so I can't use the sendto system call.
This is the code of the UDP server:
%include "../StandardLibrary/standardlib.inc"
%include "./network.inc"
PORT_NUMBER equ 4096
%macro printError 3
print %1
printLineInt %2, %3
%endmacro
%macro clearBuffer 1
getLength %1
mov r12, 0
%%loop:
mov [%1 + r12], byte 0
inc r12
cmp r11, r12
jne %%loop
%endmacro
section .data
sock_addr:
istruc sockaddr_in
at sockaddr_in.sin_family, dw AF_INET
at sockaddr_in.sin_port, dw htons(PORT_NUMBER)
at sockaddr_in.in_addr, db 00h, 00h, 00h, 00h
at sockaddr_in.sin_zero, db 0, 0, 0, 0, 0, 0, 0, 0
iend
client_sockaddr:
istruc sockaddr
at sockaddr.sa_family, dw AF_INET
at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0
iend
;Messaggi di successo
so_creation_success db "Socket creato con successo", 0
so_binding_success db "Binding del socket eseguito con successo", 0
so_started_capturing db "Socket in ascolto di messaggi...", 0
;Messaggi di errore
so_creation_error db "Errore nella creazione del socket: ", 0
so_binding_error db "Errore nel binding del socket: ", 0
so_capture_error db "Errore nella ricezione del messaggio: ", 0
so_sending_error db "Errore nell'invio del messaggio: ", 0
section .bss
buffer resb 100
socket_fd resw 1
error_code resq 1
tmp resb 10
section .text
global _start
_start:
;1) Creazione del socket
mov rax, SYS_SOCKET
mov rdi, AF_INET
mov rsi, SOCK_DGRAM
mov rdx, 0
syscall
cmp rax, -1
jle _socket_error
;Carica nello stack il file descriptor
mov [socket_fd], rax
printLine so_creation_success
;2) Binding del socket
mov rax, SYS_BIND
mov rdi, [socket_fd]
mov rsi, sock_addr
mov rdx, sockaddr_in_size
syscall
cmp rax, -1
jle _binding_error
printLine so_binding_success
;3) Ricezione dei messaggi
_listen:
mov rax, SYS_RECVFROM
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 20
mov r10, 0
mov r8, client_sockaddr
mov r9, 16
syscall
cmp rax, -1 ;Controlla che non ci siano stati errori
jle _capture_error
cmp rax, 1 ;Controlla che non sia stato ricevuto un pcchetto vuoto
jle _exit ;è definito vuoto anche un pacchetto con un solo carattere
;Messaggio ricevuto con successo
print buffer
clearBuffer buffer ;Svuota il buffer
jmp _listen
;Il programma è terminato con successo
_exit:
exit
;Errore nella creazione del socket
_socket_error:
neg rax
mov [error_code], rax
printError so_creation_error, [error_code], tmp
exit [error_code]
;Errore durante il binding del socket
_binding_error:
neg rax
mov [error_code], rax
printError so_binding_error, [error_code], tmp
exit [error_code]
;Errore durante la ricezione del messaggio
_capture_error:
neg rax
mov [error_code], rax
printError so_capture_error, [error_code], tmp
exit [error_code]
And on network.inc I have:
SYS_SOCKET equ 41 ;Id system call per la creazione del socket
SYS_SENDTO equ 44 ;Id system call per l'invio di datagram UDP
SYS_RECVFROM equ 45 ;Id system call per la ricezione di datagram UDP
SYS_BIND equ 49 ;Id system call per legare un socket ad un indirizzo IP (o ad una famiglia)
AF_INET equ 2 ;Rete IPv4
SOCK_DGRAM equ 2 ;Id del protocollo UDP
INADDR_ANY equ 0 ;Indica che il socket accetta comunicazioni da qualsiasi indirizzo IP
;Traduce x in un valore di tipo network byte order
%define htons(x) ((x >> 8) & 0xFF) | ((x & 0xFF) << 8)
;Rappresenta un indirizzo IPv4
struc in_addr
.s_addr: resb 4 ;Valore dei 4 ottetti
endstruc
;Rappresenta la struttura di un socket
struc sockaddr_in
.sin_family: resw 1 ;Id del tipo di indirizzo
.sin_port: resw 1 ;Numero di porta
.in_addr: resb 4 ;Indirizzo IP
.sin_zero: resb 8 ;Byte di rimepimento
endstruc
struc sockaddr
.sa_family resw 1
.sa_data resb 14
endstruc
As UDP client I'm using the netcat command: netcat -u 127.0.0.1 4096
You can pass a pointer to your sockaddr structure in SYS_RECVFROM according to the documentation here:
mov r8, 0
mov r9, 0
And then use filled structure with sendto syscall.
UPD
sockaddr has following shape in C:
struct sockaddr {
unsigned short int sa_family;
char sa_data[14];
};
I think, it looks something like this in NASM:
struc sockaddr
.sa_family resw 1
.sa_data db 14
endstruc
client_sock_addr:
istruc sockaddr
at sockaddr.sa_family, dw AF_INET
at sockaddr.sa_data, db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
iend
UPD #2
I've noticed that you're allocating 2 bytes for the socket. Actually, sockets must be int:
socket_fd resd 1
Next thing: you have to pass to r9 reference rather than value
section .data
; ...
client_sockaddr_len dd 16
; ...
; ...
mov rax, SYS_RECVFROM
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 20
mov r10, 0
mov r8, client_sockaddr
mov r9, client_sockaddr_len
syscall
And finally you can sendto
mov rax, SYS_SENDTO
mov rdi, [socket_fd]
mov rsi, buffer
mov rdx, 50
mov r10, 0
mov r8, client_sockaddr
mov r9, [client_sockaddr_len]
syscall
I wrote a kernel to print hello world. but it's not working,
hello world is not printed.
assembly print is working well.
I using GCC and NASM for cross compile and assemble.
This is the my bootloader, second stage code and C code.
[ORG 0x00]
[BITS 16]
section .text
JMP 0x07C0:START ;
START:
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0x0000
mov ss, ax
mov ax, 0xffff
mov sp, ax
mov bp, ax
mov si, 0xB800
mov es, si
mov bx, 0x0000
mov cx, 80*25*2
CLEAR: ;Clear screen
mov byte[es:bx], 0x00
inc bx
loop CLEAR
READ: ;Read disk
mov si, 0x1000
mov es, si
mov bx, 0x0000 ; ES:BX, 0x1000:0000
mov ah, 0x02
mov al, 2
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13
jnc A20_ENABLE ; No Error -> jump to A20_ENABLE
READERROR: ;If failed to read disk,
jmp $
A20_ENABLE: ;Enable A20 GATE with BIOS
mov ax, 0x2401
int 0x15
jnc ENTRY
A20_FAIL: ;If BIOS Interrupt is not working, use Control Port.
mov al, 2 ;
out 0x92, al
ENTRY: ;Entry Point
cli
lgdt [GDTR]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp $+2 ; for pipeline
nop
nop
jmp dword 0x08:0x10000 ; jump to second stage
GDTR: ; GDT
dw GDTEND - GDT - 1
dd GDT+0x7C00
GDT:
NULL:
dw 0x00 ;프로세서 예약구간
dw 0x00
db 0x00
db 0x00
db 0x00
db 0x00
CODE:
dw 0xFFFF ;LIMIT ADRESS
dw 0x0000 ;BASE ADRESS
db 0x00 ;BASE ADRESS
db 0x9A ;1001 1010 A code segment descriptor (for your kernel, it should have type=0x9A)
db 0xCF ;1100 1111
db 0x00
DATA:
dw 0xFFFF
dw 0x0000
db 0x01
db 0x92 ;1001 0010 A data segment descriptor (you cant write to a code segment, so add this with type=0x92)
db 0xCF ;1100 1111
db 0x00
VIDEO:
dw 0xFFFF
dw 0x8000
db 0x0B
db 0x92
db 0x40
db 0x00
GDTEND:
times 510 - ($-$$) db 0x00
db 0x55
db 0xAA
Second stage.
[ORG 0x10000]
[BITS 32]
PROTECTED:
mov ax, 0x10
mov gs, ax
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
xor esp, esp ; make stack
mov esp, 0xFFFE
mov ebp, 0xFFFE
mov si, 0x18 ; setting for print message
mov es, si
mov bx, 0
lea si, [message]
mov ah, 0x0f
MESSAGE: ; print loop
lodsb
cmp al, 0
je LOADKERNEL
mov [es:bx], ax
add bx, 2
jmp MESSAGE
LOADKERNEL:
jmp dword 0x08:0x10200 ; jump to C kernel
message: db 'Protected Mode Entered.', 0
times 512 - ($ - $$) db 0x00
and C code also.
void write_string();
void main(){
write_string(0x09, "helloworld");
}
void write_string( int colour, const char *string )
{
volatile char *video = (volatile char*)0xB8000;
while( *string != 0 )
{
*video++ = *string++;
*video++ = colour;
}
}
I converted C code to assembly with ld, gcc and objcopy.
gcc -c -m32 -ffreestanding kernel.c -o kernel.o
ld -melf_i386 -Ttext 0x10200 -nostdlib kernel.o -o kernel.tmp
objcopy -O binary kernel.tmp kernel.img
cat bootloader.img secondstage.img kernel.img > Disk
like this.
I really can't understand why it's not working.
If you need more information, plz leave a comment.
My objective is to open a socket, connect to that socket on a port, and then whenever that socket sends data to me, I want to write it to stdout.
My code works fine, but for some reason the string that is writing doesn't null terminate? I'm not so sure what the issue is and I have yet to find anything online about it.
#socket(2,1,0)
li $t7, -6
nor $t7, $t7, $zero
addi $a0, $t7, -3
addi $a1, $t7, -3
slti $a2, $zero, -1
li $v0, 4183
syscall 0x40404
sw $v0, -4($sp)
#connect(3, &addr, 16)
lw $a0, -4($sp)
li $t7, -3
nor $t7, $t7, $zero
sw $t7,-32($sp)
lui $t6,0x7a69
ori $t6,$t6,0x7a69
sw $t6, -28($sp)
lui $t5, 0x7F00
ori $t5, $t5, 0x1
sw $t5, -26($sp)
addiu $a1, $sp, -30
li $t4, -17
nor $a2, $t4, $zero
li $v0, 4170
syscall 0x40404
#read(3, addr, 50)
nex:
lw $a0, -4($sp)
addiu $a1, $sp,-64
li $a2, 50
addi $a2, $a2, -1
li $v0, 4003
syscall 0x40404
beqz $a3, next
move $a2,$v0
negu $a2,$v0
next:
blez $a2,nextt
#write(1, addr, 50)
li $a0, 1
li $v0, 4004
syscall 0x40404
j nex
nextt:
Here is the strace of me writing a\n to the socket, and it writing a\n\0\0\0\0\0\0\0\0 to stdout.
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(31337), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
read(3, "a\n", 49) = 2
write(1, "a\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 49a
zi3▒ݤ) = 49
As you can see, it prints out some weird characters at the end. Thanks for any help!
Notice in your strace output that your read syscall returns a value of 2 [in $v0].
Also notice that your write syscall has a length much greater than 2.
After your read syscall you have:
move $a2,$v0 # this reg has 2
negu $a2,$v0 # BUG: this changes it to -2 (0xFFFFFFFE)
The length for the write was interpreted as an unsigned number [and a very large one]
Change this to just [i.e. remove the negu]:
move $a2,$v0
I am using the latest Openwrt trunk firmware (kernel 4.3) and have successfully compiled the driver for its CryptoEngine, an internal ipsec accelerator of MT7621 Soc (which as far as I understood is on an internal bus called AMBA / APB).
The driver seems to work (CryptoEngine gets configured successfully by sending and receiving packets, so it does "react" on memory mapped registers).
However the driver is unable to hook up the irq (so it works only in polling mode), because request_irq always fails with error code 89 (EDESTADDRREQ Destination address required) on IRQ 19 (driver log does not give any error before) and I can't understand why (I am not a linux device driver expert, just a basic understanding, let's say that I managed to recompile the driver).
Any idea why this happens ? Could it be a problem with the board dts file ? Here follows the dmesg log and a link to the dts files used (Dmesg Log too long to include, Soc dtsi (dev.openwrt.org/browser/trunk/target/linux/ramips/dts/mt7621.dtsi) and Board dts))
CryptoEngine memory map 0x1E004000-0x1E004FFF
root#OpenWrt:/# lspci
00:00.0 PCI bridge: Device 0e8d:0801 (rev 01)
00:01.0 PCI bridge: Device 0e8d:0801 (rev 01)
00:02.0 PCI bridge: Device 0e8d:0801 (rev 01)
01:00.0 Network controller: MEDIATEK Corp. MT7662E 802.11ac PCI Express Wireless Network Adapter
02:00.0 Network controller: MEDIATEK Corp. MT7662E 802.11ac PCI Express Wireless Network Adapter
03:00.0 SATA controller: ASMedia Technology Inc. ASM1062 Serial ATA Controller (rev 01)
root#OpenWrt:/# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
8: 63126 63253 63143 63987 MIPS GIC Local 1 timer
10: 1513 0 0 0 MIPS GIC 10 1e100000.ethernet
11: 2 0 0 0 MIPS GIC 11 mt76pci
29: 0 0 0 0 MIPS GIC 29 xhci-hcd:usb1
31: 2 0 0 0 MIPS GIC 31 mt76pci
33: 7976 0 0 0 MIPS GIC 33 serial
63: 1178 0 0 0 MIPS GIC 63 IPI call
64: 0 1305 0 0 MIPS GIC 64 IPI call
65: 0 0 1257 0 MIPS GIC 65 IPI call
66: 0 0 0 1087 MIPS GIC 66 IPI call
67: 144709 0 0 0 MIPS GIC 67 IPI resched
68: 0 25822 0 0 MIPS GIC 68 IPI resched
69: 0 0 97134 0 MIPS GIC 69 IPI resched
70: 0 0 0 43037 MIPS GIC 70 IPI resched
root#OpenWrt:/# cat /proc/ioports
ffffffff-ffffffff : /pcie#1e140000