How to read or write an I/O port in arm64? - io

I was trying to write one byte to an I/O port, but I am failing finding the correct instructions in the instruction set of arm64 aarch64 architecture.
To do the same thing in i386, I'd do something of similar:
void dbg_io_write_8(uint16_t port, uint8_t val)
{
asm volatile (
"outb %%al, %%dx;"
/* Outputs */ : /* None */
/* Inputs */ : "a" (val), "d" (port)
/* Clobbers */ : /* None */
);
}
And, for reading:
uint8_t dbg_io_read_8(uint16_t port)
{
uint8_t val;
asm volatile (
"inb %%dx, %%al;"
/* Outputs */ : "=a" (val)
/* Inputs */ : "d" (port)
/* Clobbers */ : /* None */
);
return val;
}
This code refers to the NS16550 serial port.

arm64 has no concept of I/O ports.
If you are both a CPU vendor and a SoC designer, and have a very good reason for it, you might opt to map a peripheral to a (set of) custom system register(s). In that case you'd talk to the peripheral via mrs/msr.
In all other cases, you memory-map it to some physical address. So you access it through simple loads and stores.

Related

What is the relationship between an Interrupt Service Routine and a Device Driver that registers to an IRQ?

I have recently be reading up on how to write Linux Device Drivers and came across a Device Driver which basically "handles" IRQ1 (a keyboard event). The code itself is shown below:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
/* This function services keyboard interrupts */
irq_handler_t irq_handler (int irq, void *dev_id, struct pt_regs *regs) {
static unsigned char scancode;
/* Read keyboard status */
scancode = inb (0x60);
if ((scancode == 0x01) || (scancode == 0x81)) {
printk("You pressed Esc !\n");
}
return (irq_handler_t) IRQ_HANDLED;
}
/* Initialize the module and Register the IRQ handler */
static int __init keybrd_int_register(void) {
int result;
/* Request IRQ 1, the keyboard IRQ */
result = request_irq (
1,
(irq_handler_t) irq_handler,
IRQF_SHARED,
"keyboard_stats_irq",
(void *)(irq_handler)
);
if (result) {
printk(KERN_INFO "can't get shared interrupt for keyboard\n");
}
return result;
}
/* Remove the interrupt handler */
static void __exit keybrd_int_unregister(void) {
free_irq(1, (void *)(irq_handler)); /* can't pass NULL, this is a shared interrupt handler! */
}
MODULE_LICENSE ("GPL");
module_init(keybrd_int_register);
module_exit(keybrd_int_unregister);
I would like to understand whether this Device Driver is considered an "Interrupt Service Routine"? Correct me if I'm wrong, but the flow of events is as follows:
Press key on Keyboard
Keyboard Triggers IRQ1
Operating System looks up ALL the Interrupt Service Routines "registered" to IRQ1 (which includes the Device Driver we wrote above) and triggers the code in these Interrupt Service Routines (including the code in Device Driver above).
Does this mean tat the Device Driver IS the Interrupt Service Routine?
A follow up question would be, how would you actually write a real Keyboard Driver (not one that just naively prints out some value)? How do you tell the Operating System that a particular key was pressed and that it should inform the current running application that this key was pressed? What does the C code look like for this?

Where the structure "struct page" is stored on the linux kernel?

I learned that linux kernel manages the memory and the unit for allocate/deallocate the memory is 4KB, which is the page size. And I know that this pages are handled by struct page.
I got a actual code here.
struct page {
unsigned long flags; /* Atomic flags, some possibly
* updated asynchronously */
/*
* Five words (20/40 bytes) are available in this union.
* WARNING: bit 0 of the first word is used for PageTail(). That
* means the other users of this union MUST NOT use the bit to
* avoid collision and false-positive PageTail().
*/
union {
struct { /* Page cache and anonymous pages */
/**
* #lru: Pageout list, eg. active_list protected by
* pgdat->lru_lock. Sometimes used as a generic list
* by the page owner.
*/
struct list_head lru;
/* See page-flags.h for PAGE_MAPPING_FLAGS */
struct address_space *mapping;
pgoff_t index; /* Our offset within mapping. */
/**
* #private: Mapping-private opaque data.
* Usually used for buffer_heads if PagePrivate.
* Used for swp_entry_t if PageSwapCache.
* Indicates order in the buddy system if PageBuddy.
*/
unsigned long private;
};
struct { /* page_pool used by netstack */
/**
* #dma_addr: might require a 64-bit value even on
* 32-bit architectures.
*/
dma_addr_t dma_addr;
};
struct { /* slab, slob and slub */
union {
struct list_head slab_list;
struct { /* Partial pages */
struct page *next;
#ifdef CONFIG_64BIT
int pages; /* Nr of pages left */
int pobjects; /* Approximate count */
#else
short int pages;
short int pobjects;
#endif
};
};
struct kmem_cache *slab_cache; /* not slob */
/* Double-word boundary */
void *freelist; /* first free object */
union {
void *s_mem; /* slab: first object */
unsigned long counters; /* SLUB */
struct { /* SLUB */
unsigned inuse:16;
unsigned objects:15;
unsigned frozen:1;
};
};
};
struct { /* Tail pages of compound page */
unsigned long compound_head; /* Bit zero is set */
/* First tail page only */
unsigned char compound_dtor;
unsigned char compound_order;
atomic_t compound_mapcount;
};
struct { /* Second tail page of compound page */
unsigned long _compound_pad_1; /* compound_head */
atomic_t hpage_pinned_refcount;
/* For both global and memcg */
struct list_head deferred_list;
};
struct { /* Page table pages */
unsigned long _pt_pad_1; /* compound_head */
pgtable_t pmd_huge_pte; /* protected by page->ptl */
unsigned long _pt_pad_2; /* mapping */
union {
struct mm_struct *pt_mm; /* x86 pgds only */
atomic_t pt_frag_refcount; /* powerpc */
};
#if ALLOC_SPLIT_PTLOCKS
spinlock_t *ptl;
#else
spinlock_t ptl;
#endif
};
struct { /* ZONE_DEVICE pages */
/** #pgmap: Points to the hosting device page map. */
struct dev_pagemap *pgmap;
void *zone_device_data;
/*
* ZONE_DEVICE private pages are counted as being
* mapped so the next 3 words hold the mapping, index,
* and private fields from the source anonymous or
* page cache page while the page is migrated to device
* private memory.
* ZONE_DEVICE MEMORY_DEVICE_FS_DAX pages also
* use the mapping, index, and private fields when
* pmem backed DAX files are mapped.
*/
};
/** #rcu_head: You can use this to free a page by RCU. */
struct rcu_head rcu_head;
};
union { /* This union is 4 bytes in size. */
/*
* If the page can be mapped to userspace, encodes the number
* of times this page is referenced by a page table.
*/
atomic_t _mapcount;
/*
* If the page is neither PageSlab nor mappable to userspace,
* the value stored here may help determine what this page
* is used for. See page-flags.h for a list of page types
* which are currently stored here.
*/
unsigned int page_type;
unsigned int active; /* SLAB */
int units; /* SLOB */
};
/* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
atomic_t _refcount;
#ifdef CONFIG_MEMCG
struct mem_cgroup *mem_cgroup;
#endif
/*
* On machines where all RAM is mapped into kernel address space,
* we can simply calculate the virtual address. On machines with
* highmem some memory is mapped into kernel virtual memory
* dynamically, so we need a place to store that address.
* Note that this field could be 16 bits on x86 ... ;)
*
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
int _last_cpupid;
#endif
} _struct_page_alignment;
And I have no idea where the linux kernel stores this huge(?) structure.
There are a lot of pages handled in the linux kernel and that means that we have a lot of this struct page structures. Where can it be stored on the memory?
Also I have no idea what the union up there is up for.
First, there are several memory models like FMM, SMP, and NUMA. The knowledge about virtual memory, page and page tables, sturct of kernel and user space memory may not write here because there are too much more than the limitation of the answer's length and I think you can learn it from any books.
Let's use NUMA as an example. In NUMA, every CPU will have a node : struct pglist_data *node_data, and this struct has many Zones like ZONE_DMA, ZONE_DMA32, ZONE_NORMAL, ZONE_HIGHMEM, ZONE_MOVALBE, every Zone has many struct free_area, and this struct includes a list of struct page.
Why we need to use page? It is because our physical memory is limit, so we create virtual memory. And then we need a mechanism to load virtual memory to physical memory to run the task(process or thread). So we use page as the meta entry, and use some models like NUMA to control those pages and page tables.
When we need to allocate some memory, we will use buddy system and slab/slub allocator to allocate memory pages and add them to a zone that can use. When the physical memory loads too much pages, it will use get_page_from_freelist() or kswapd to swap some out.
Auctally, memory and addresses space is a essential part of Linux kernel. I suggest you to read some books like CSAPP to get a relatively deep understand of OS, and then reading Linux kernel source codes to dive into them.
I have the same question as you recently.
Where is the struct page in Linux kernel?
And I think I can give you some helpful informations. Every node stores pages into pg_data_t's node_mem_map, and there is a global variable mem_map, which is pointing to Node 0's page array.
You can find more details from Professional Linux Kernel Architecture. In chapter 3, the section is "Creating Data Structures for Each Node".
If you want to know exact place where it is stored, this article might give you the answer.
It says that they are "usually stored at the beginning of ZONE_NORMAL".

USB to Serial adapter - receive works OK for a limited time

I borrowed much of the following code from the Linux Serial HOWTO pages to create my program to read characters endlessly from the serial port. I'm using a USB to serial port adapter (made by micro innovations) so I can get a serial port. Linux recognizes the device as /dev/ttyUSB0.
When I use the device in other native linux programs (such as the output dump program), the adapter works flawlessly and data continuously comes in. However, with my program, the data comes in for roughly 10 seconds, then I receive no more data, yet I know the hardware that the adapter is connected to has more data to process.
Is there something in this code I can fix to allow me to receive unlimited characters as they arrive without me having to restart the program to receive more characters?
I still want to make the mainline code asynchronous to the serial routine as I will be adding more code later on.
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>
#define BAUDRATE B57600
#define MODEMDEVICE "/dev/ttyUSB0" /* My USB to serial converter */
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
void signal_handler_IO (); /* definition of signal handler */
int wait_flag=TRUE; /* TRUE while no signal received */
int main(){
int fd,res;
struct termios oldtio,newtio;
struct sigaction saio; /* definition of signal action */
char buf[255];
/* open the device */
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
if (fd <0) {perror(MODEMDEVICE); return -1; }
/* install the signal handler before making the device asynchronous */
saio.sa_handler = signal_handler_IO;
saio.sa_flags=0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
/* allow the process to receive SIGIO */
fcntl(fd, F_SETOWN, getpid());
/* Make the file descriptor asynchronous */
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio); /* save current port settings */
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VMIN]=1;
newtio.c_cc[VTIME]=0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/* loop while waiting for input. */
while (1) {
usleep(100000);
/* after receiving SIGIO, wait_flag = FALSE,
input is available and can be read */
if (wait_flag==FALSE) {
res=0;
/* Read up to 11 bytes which is remote packet length if file handle is valid */
if (fd){
res = read(fd,buf,11);
}
/* If there's at least 2 bytes (character plus ending null) then process it */
if (res > 1){
int n;
/* Print each character as 2-digit hex even if value is 0. */
for (n=0;n<(res-1);n++){
printf("%2X ",(unsigned char)buf[n]);
}
/* Print total number of characters received */
printf(" = %d\n",res);
}
wait_flag = TRUE; /* wait for new input */
}
/* Return to start of endless loop */
}
return 0;
}
/***************************************************************************
* signal handler. sets wait_flag to FALSE, to indicate above loop that *
* characters have been received. *
***************************************************************************/
void signal_handler_IO ()
{
wait_flag = FALSE;
}
Classic race condition:
The signal_handler_IO() may be called before your code sets wait_flag = TRUE; e.g. during those printf() calls.
At this point your program will simply stop.
A remedy could be setting the wait_flag immediately after the if - but I would recommend looking into linux poll() call for async I/O.

DOS: how to get current country code or code page in Turbo C

How to get some locale-specific information where the country can be detected? E.g. current country code, keyboard layout or code page in Turbo C in DOS environment?
If that is not possible with Turbo C library functions, some BIOS calls could do that (INT 21)?.
KEYB program is able to show e.g. keyboard layout. That would be more than enough for my purposes:
https://www.dosbox.com/wiki/KEYB
Use INT 21h, AX=6501h. Here's code for Turbo C:
#include <dos.h>
#include <stdio.h>
struct country_info_buffer
{
unsigned char info_id;
unsigned short buffer_size;
unsigned short country_id;
unsigned short code_page;
struct COUNTRY country_info;
};
int main()
{
/* Registers for INT21 call */
union REGS regs;
struct SREGS sregs;
/* Output buffer */
struct country_info_buffer info;
/* Get current value of segment registers */
segread(&sregs);
/* Get extended country information / general internationalization info */
regs.x.ax = 0x6501;
/* Global code page */
regs.x.bx = 0xFFFF;
/* Current country */
regs.x.dx = 0xFFFF;
/* Size of output buffer */
regs.x.cx = sizeof(info);
/* Pointer to output buffer goes to ES:DI */
sregs.es = FP_SEG(&info);
regs.x.di = FP_OFF(&info);
/* Call int21 */
intdosx(&regs, &regs, &sregs);
if (regs.x.cflag)
{
printf("Call failed, ax=%d\n", regs.x.ax);
return 1;
}
printf("Country code: %d, Code page: %d\n", info.country_id, info.code_page);
return 0;
}
Btw. Ralf Brown's Interrupt List is great resource for DOS system calls. Too bad it stopped being maintained even before DOS ran out of favors, so some "newest" stuff isn't described there.

how to intercept IP packets on linux

I am trying to achieve this goal on Linux (not sure if it is possible and how if so):
I need to write a program to intercept all IP packets on an interface, and deliver this packet to a specific set of user-space programs. By intercept I mean, an IP packet is captured by my program (maybe a kernel module or a special user-space program), then this packet does not go through IP stack anymore. For example, let's say the OS is running a lot of processes (either in kernel-space or user-space), A, B, C, D..., etc. If an IP packet is received at an interface, say eth2, I only want A,B sees this packet, and all other processes do even know the existence of this packet.
Can someone put me into the right direction? Thanks a lot!
What I suggest is that you re-examine if it's really necessary to intercept packets in the way you describe. This sounds like you have not completely understood, as pointed above, how networking works.
First of all unless your programs somehow magically manage to read raw network packets instead of using standard sockets, they're not receiving traffic destined to each other anyway. Each socket has a port associated with it and only one process can bind onto same port in the same host (the socket actually being nothing more than a pair of a port and a host address).
In case you're actually reading raw network packets in your programs and this is necessary, you most likely should not be running them on the same host. Instead use virtualization to put programs which aren't allowed to see packets destined to each other in different virtual hosts and thus separate them completely instead of using a rather complex programmatic solution.
If everything else fails, you might want to take a good look at libpcap which allows you to capture network packets, even from several programs simultaneously. Albeit you'd have to run those as root but that's necessary anyway for being able to put a network interface into promiscuous mode and so on and so fort. Here is a simple example for reading little something from the network, you can find plenty more from libpcap homepage and associated
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pcap.h>
/* IP header (from tcpdump examples) */
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
/* callback function for pcap_loop */
void cllbck(u_char * args,
const struct pcap_pkthdr *hdr, const u_char * pkt)
{
const struct sniff_ip *ip = (struct sniff_ip *) (pkt + 14);
fprintf(stderr, "Sniffed a packet with length %d.\n", hdr->len);
fprintf(stderr, "IP version %d.\n", ip->ip_vhl >> 4);
}
int main(int argc, char *argv[])
{
char *dev; /* device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* buffer for libpcap errmsgs */
pcap_t *cap; /* libpcap capture session */
char *filt = "host 127.0.0.1"; /* capture filter */
struct bpf_program fp; /* compiled filter */
struct pcap_pkthdr hdr; /* packet header from libpcap */
const u_char *pkt; /* packet from libpcap */
dev = strdup(argv[1]);
if (dev == NULL) {
fprintf(stderr, "Invalid device.\n");
return 2;
}
/* open the device for live capture */
cap = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (cap == NULL) {
fprintf(stderr, "Opening device `%s´ failed: %s\n", dev, errbuf);
return 2;
}
/* compile the capture filter */
if (pcap_compile(cap, &fp, filt, 0, PCAP_NETMASK_UNKNOWN) < 0) {
fprintf(stderr, "Failed to parse filter `%s´: %s\n",
filt, pcap_geterr(cap));
return 2;
}
/* set the filter active for this session */
if (pcap_setfilter(cap, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filt, pcap_geterr(cap));
return 2;
}
/* pcap close will loop until an error if 2nd arg is < 0 */
pcap_loop(cap, -1, cllbck, NULL);
/* end session, pcap_loop has exited ie. an error has occurred */
pcap_close(cap);
return 0;
}
/* end of file */

Resources