I am trying to access data from a file that I load in via tftp. I'm using an AM3358 processor
tftp 81000000 mydata
and I can see the data being correctly loaded
=> md 81000000
81000000: 00004000 00000000 00002000 00000400 .#....... ......
In the u-boot code, I create a pointer to this address and then attempt to de-reference it, but the value is incorrect which makes me think I'm using the incorrect address
unsigned long addr = 0x81000000;
uint32_t *ptr = &addr;
uint32_t val = *(ptr+0);
printf("addr %ul val: %ul", addr, val);
Furthermore, I'm trying to load the address of mydata into a 32-bit LCD register, but the physical address of 0x81000000 is beyond that of a 32-bit number. I believe I'm just confused as to what address mapping is involved here.
bdi yields
=> bdi
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
baudrate = 115200 bps
TLB addr = 0x9fff0000
relocaddr = 0x9ffb4000
reloc off = 0x1f7b4000
irq_sp = 0x9df8ba90
sp start = 0x9df8ba80
Early malloc usage: 4a8 / 1000
fdt_blob = 0x9df8bea0
Why would 0x81000000 not be a valid 32 bit number ?
0x00000000 <= 0x81000000 <= 0xFFFFFFFF.
I think there may be an error in your logic: you are initializing ptrwith the address of addr, not the address of its content.
The correct code would rather be something like:
uint32_t addr = 0x81000000;
uint32_t *ptr = (uint32_t*)(uintptr_t) addr;
uint32_t val = *(ptr+0);
printf("addr %ul val: %ul", addr, val);
This can be tested on your PC - you may need to add support for building 32 bit applications, i.e. execute sudo apt-get install gcc-multilib on Ubuntu.
ptr.c:
#include <stdio.h>
#include <stdint.h>
int
main ()
{
uint32_t addr = 0x81000000; // gcc 9.3.0 is complaining about uint32_t *ptr = &addr;
// your code
{
uint32_t *ptr = &addr;
printf ("%p\n", ptr);
}
// correct code
{
uint32_t *ptr = (uint32_t *) (uintptr_t) addr;
printf ("%p\n", ptr);
}
}
gcc -m32 -o ptr ptr.c
./ptr
0xff83bc60
0x81000000
This would be why you cannot access the content of the file you transferred using TFTP, you are reading from an incorrect, but valid address.
Related
This is a follow-up from the following question Custom Instruction crashing with SIGNAL 4 (Illegal Instruction): RISC-V (32) GNU-Toolchain with QEMU (apologies if I have missed any etiquette points in advance or formatted this in an unsavoury way, as I am still new to posting here). I am using the latest version of the riscv-gnu-toolchain, so I am just wondering what caveats and differences this process will be for this platform.
I was following namely two guides to add custom RISC-V instructions to QEMU - namely https://www.ashling.com/wp-content/uploads/QEMU_CUSTOM_INST_WP.pdf and https://chowdera.com/2021/04/20210430120004272q.html.
After editing qemu/target/riscv/insn32.decode with a free opcode (following Ashling tutorial), and implementing translator function in insn_trans/trans_rvi.c.inc - QEMU hangs when calling the function as an .insn directive (I know this to work - ). Changing opcodes of existing instructions to test if disassembly changes indicated to me that QEMU was not registering the changes I made and that I didn't rebuild/recompile QEMU correctly. I simply ran make clean, reconfigured and make again in the QEMU directory to rebuild it - Is this the correct way to rebuild QEMU in this toolchain or is there something I missed.
The code to call
#include <stdio.h>
static int test_ins(int a, int b) {
int result;
asm volatile(".insn r 0x33, 7, 0x20, %0, %1, %2" : "=r"(result) : "r"(a), "r"(b));
return result;
}
int main() {
int a, b, result;
a = 2;
b = 4;
result = test_ins(a,b);
printf("%d\n", result);
}
Instruction in insn32.decode is as follows:
OPCODE = "0110011", FUNCT3 = "111" and FUNCT7 = "0100000".
Implementation of aforementioned instruction is as follows:
static bool trans_bitcnt(DisasContext *ctx, arg_bitcnt *a)
{
TCGLabel *loop_source1 = gen_new_label();
TCGLabel *loop_source2 = gen_new_label();
TCGv source1, source2, dstval, cntval;
source1 = tcg_temp_local_new();
source2 = tcg_temp_local_new();
dstval = tcg_temp_local_new();
cntval = tcg_temp_local_new();
// Count all the bits set in rs1 and rs2 and put that number in rd
gen_get_gpr(source1, a->rs1);
gen_get_gpr(source2, a->rs2);
tcg_gen_movi_tl(cntval, 0x0);
/* Count the bits that are set in the first register */
gen_set_label(loop_source1);
tcg_gen_andi_tl(dstval, source1, 0x1);
tcg_gen_shri_tl(source1, source1, 0x1);
tcg_gen_add_tl(cntval, cntval, dstval);
tcg_gen_brcondi_tl(TCG_COND_NE, source1, 0x0, loop_source1);
/* Count the bits that are set in the second register */
gen_set_label(loop_source2);
tcg_gen_andi_tl(dstval, source2, 0x1);
tcg_gen_shri_tl(source2, source2, 0x1);
tcg_gen_add_tl(cntval, cntval, dstval);
tcg_gen_brcondi_tl(TCG_COND_NE, source2, 0x0, loop_source2);
/* Update the destination register with the bits total */
gen_set_gpr(a->rd, cntval);
tcg_temp_free(source1);
tcg_temp_free(source2);
tcg_temp_free(dstval);
tcg_temp_free(cntval);
return true;
}
In short:
I am trying to run the sel4 microkernel inside a x86_64 virtual machine and can't get the ethernet interface working.
What is the correct procedure to get internet connectivity (via a vitio-net ethernet device) on a sel4 microkernel? And what are the correct (memory) addresses?
Long version:
I have tried the camkes (picoserver) examples with the e1000 netdevice but couldn't get them to work so I decided to learn some new things and start from scratch. Also I decided to use virtio-net(together with vhost) instead of an emulated e1000 device for better performance. My plan is to use ethif_virtio_pci_init to initialise a eth_driver struct and then pass the struct on to picoTCP. For now I can find the virtio PCI device in sel4 but I am unsure how to correctly access it and create the ethif_virtio_pci_config_t needed for ethif_virtio_pci_init.
Some information from libethdrivers virtio_pci.h:
typedef struct ethif_virtio_pci_config {
uint16_t io_base;
void *mmio_base;
} ethif_virtio_pci_config_t;
/**
* This function initialises the hardware and conforms to the ethif_driver_init
* type in raw.h
* #param[out] eth_driver Ethernet driver structure to fill out
* #param[in] io_ops A structure containing os specific data and
* functions.
* #param[in] config Pointer to a ethif_virtio_pci_config struct
*/
int ethif_virtio_pci_init(struct eth_driver *eth_driver, ps_io_ops_t io_ops, void *config);
so for the ethif_virtio_pci_config_t I need an uint16_t io_base address and a pointer to the MMIO base.
This is the information I have obtained so far:
Found virtio_net_pci device
BASE_ADDR[0] ----
base_addr_space[0]: 0x1 [PCI_BASE_ADDRESS_SPACE_IO]
base_addr_type[0]: 0x0 [ 32bit ]
base_addr_prefetchable[0]: no
base_addr[0]: 0xc000
base_addr_size_mask[0]: 0xffffffe0
BASE_ADDR[1] ----
base_addr_space[1]: 0x0 [PCI_BASE_ADDRESS_SPACE_MEMORY]
base_addr_type[1]: 0x0 [ 32bit ]
base_addr_prefetchable[1]: no
base_addr[1]: 0xfeb91000
base_addr_size_mask[1]: 0xfffff000
BASE_ADDR[2] ----
BASE_ADDR[3] ----
BASE_ADDR[4] ----
base_addr_space[4]: 0x0 [PCI_BASE_ADDRESS_SPACE_MEMORY]
base_addr_type[4]: 0x4 [ 64bit ]
base_addr_prefetchable[4]: yes
base_addr[4]: 0xfe000000
base_addr_size_mask[4]: 0xffffc000
BASE_ADDR[5] ----
As far as I understand I now need to map the pysical address to a virtual one. For that I created an IO-mapper but I am not sure what to map. The whole dma region starting at 0x8000000 or just the address of the virtio device? As far as I understand the new virtual address would be my MMIO base pointer but what is the uint16_t io_base than?
This is my code so far, the part I am unsure about is at the end:
#define ALLOCATOR_STATIC_POOL_SIZE ((1 << seL4_LargePageBits) * 10)
static simple_t simple;
static ps_io_mapper_t io_mapper;
static char allocator_mem_pool[ALLOCATOR_STATIC_POOL_SIZE];
static vka_t vka;
static vspace_t vspace;
static sel4utils_alloc_data_t data;
static ltimer_t timer;
int main() {
PRINT_DBG("Hello World\n");
seL4_BootInfo *info = platsupport_get_bootinfo();
simple_default_init_bootinfo(&simple, info);
/* print out bootinfo and other info about simple */
// simple_print(&simple);
allocman_t *allocman = bootstrap_use_current_simple(&simple, ALLOCATOR_STATIC_POOL_SIZE, allocator_mem_pool);
if (allocman == NULL) {
ZF_LOGF("Failed to create allocman");
}
allocman_make_vka(&vka, allocman);
int error = sel4utils_bootstrap_vspace_with_bootinfo_leaky(&vspace,
&data, simple_get_pd(&simple),
&vka, info);
if (error != 0) {
PRINT_DBG("Failed to create virtual memory manager. Error: %d\n", error);
return -1;
}
error = sel4platsupport_new_io_mapper(&vspace, &vka, &io_mapper);
if (error != 0) {
PRINT_DBG("Failed to create io mapper. Error: %d\n", error);
return -1;
}
ps_io_ops_t io_ops;
error = sel4platsupport_new_io_ops(&vspace, &vka, &simple, &io_ops);
if (error != 0) {
PRINT_DBG("Failed to create io ops. Error: %d\n", error);
return -1;
}
ps_io_port_ops_t port_ops;
int error = sel4platsupport_get_io_port_ops(&port_ops, &simple, &vka);
if (error != 0) {
PRINT_DBG("Failed to find io port ops. Error: %d\n", error);
return -1;
}
printf("Start scannning\n");
libpci_scan(port_ops);
PRINT_DBG("Found %u devices\n", libpci_num_devices);
for (uint32_t i = 0; i < libpci_num_devices; ++i) {
PRINT_DBG("PCI device %u. Vendor id: %x. Device id: %x\n",
i, libpci_device_list[i].vendor_id, libpci_device_list[i].device_id);
}
libpci_device_t* virtio_net_pci = libpci_find_device(0x1af4, 0x1000);
if (!virtio_net_pci) {
PRINT_DBG("Failed to find the virtio_net_pci device\n");
// return -1;
}else{
// libpci_device_iocfg_debug_print(&virtio_net_pci->cfg,true);
PRINT_DBG("Found virtio_net_pci device\n");
libpci_device_iocfg_debug_print(&virtio_net_pci->cfg,false);
}
//Now what?
unsigned long phys = 0x8000000; //what physical address to map?
void *mmio_ptr = ps_io_map(&io_mapper, phys, 4096, 0, PS_MEM_NORMAL);
memset(ptr, 0, 4096);
if (mmio_ptr == NULL) {
PRINT_DBG("Failed to map phys addr. Error: %p\n", ptr);
return -1;
}
ethif_virtio_pci_config_t me_config;
me_config.mmio_base = mmio_ptr; //is this correct?
//me_config.io_base = ?
I read alot about the sel4 kernel but I am still new to most of the concepts of the sel4 microkernel (and Linux kernel) so I am very grateful for any tipps and recommendations. I am normally working with embedded, microcontrollers and more "bare metal" platforms and wanted to learn something new but for now alot is very confusing.
I'm writing a FAT16 driver in GNU C for a hobby operating system, and I have a structure defined as such:
struct directory_entry {
uint8_t name[11];
uint8_t attrib;
uint8_t name_case;
uint8_t created_decimal;
uint16_t created_time;
uint16_t created_date;
uint16_t accessed_date;
uint16_t ignore;
uint16_t modified_time;
uint16_t modified_date;
uint16_t first_cluster;
uint32_t length;
} __attribute__ ((packed));
I was under the impression that name would be at the same address as the whole struct, and that attrib would be 11 bytes after that. And indeed, (void *)e.name - (void *)&e is 0 and (void *)&e.attrib - (void *)&e is 11, where e is of type struct directory_entry.
In my kernel, a void pointer to e is passed to a function which reads its contents from a disk. After this function, *(uint8_t *)&e is 80 and *((uint8_t *)&e + 11 is 8, as expected for what's on the disk. However, e.name[0] and e.attrib both are 0.
What gives here? Am I misunderstanding how __attribute__ ((packed)) works? Other structs with the same attribute work how I expect at other parts of my kernel. I can post a link to the full source if needed.
Edit: The full source is in this gitlab repository, on the stack-overflow branch. The relevant part is lines 34 to 52 of src/kernel/main.c. I'm sure that the data is being populated right, as I check *(uint8_t *)&e and *((uint8_t *)&e + 11). When I run it, the following is output by that part:
(void *)e.name - *(void *)&e
=> 0
*(uint8_t *)&e
=> 80
e.name[0]
=> 0
(void *)&e.attrib - (void *)&e
=> 11
*((uint8_t *)&e + 11)
=> 8
e.attrib
=> 0
I'm very confused about why e.name[0] would be any different than *(uint8_t *)&e.
Edit 2: I disassembled this part using objdump, to see what the difference was in the compiled code, but now I'm even more confused.
u8_dec(*(uint8_t *)&e, nbuf); and u8_dec(e.name[0], nbuf); are both compiled to: (comments mine)
lea eax, [ebp - 0x30] ;loads address of e from stack into eax
movzx eax, byte [eax] ;loads byte pointed to by eax into eax, zero-extending
movzx eax, al ;not sure why this is here, as it's already zero-extended
sub esp, 0x8
push 0x31ce0 ;nbuf
push eax ;the byte we loaded
call 0x3162f ;u8_dec
add esp, 0x10
This passes in the first byte of the struct, as expected. I'm sure that u8_dec doesn't modify e, as its first argument is passed by value and not by reference. nbuf is an array declared at file-scope, while e is declared at function scope, so it's not that they overlap or anything. Perhaps u8_dec isn't doing its job right? Here's the source of that:
void u8_dec(uint8_t n, uint8_t *b) {
if (!n) {
*(uint16_t *)b = '0';
return;
}
bool zero = false;
for (uint32_t m = 100; m; m /= 10) {
uint8_t d = (n / m) % 10;
if (zero)
*(b++) = d + '0';
else if (d) {
zero = true;
*(b++) = d + '0';
}
}
*b = 0;
}
It's pretty clear now that packed structs do work how I think they do, but I'm still not sure what's causing the problem. I'm passing the same value to a function that should be deterministic, but I'm getting different results on different calls.
My kernel utilizes 32-bit protected mode segmenting. I had my data segment as 0x0000.0000 - 0x000f.ffff and my stack segment as 0x0003.8000 - 0x0003.ffff, to trigger a general protection fault if the stack over overflowed, rather than allowing it to overflow into other kernel data and code.
However, when GCC compiles C code, it assumes that the stack and data segments have the same base, as this is most often the case. This was causing a problem as when I took the address of the local variable, it was relative to the stack segment (as local variables are on the stack), but when I dereferenced the pointer in the function that was called, it was relative to the data segment.
I have changed my segmenting model so that the stack is in the data segment instead of its own segment, and this has fixed the problem.
Consider the following piece of code:
pgd_t *pgd;
pte_t *ptep;
pud_t *pud;
pmd_t *pmd;
char *addr;
struct page *page = NULL;
struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd) || pgd_bad(*pgd))
goto out;
printk(KERN_NOTICE "Valid pgd");
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || pud_bad(*pud))
goto out;
printk(KERN_NOTICE "Valid pud");
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd) || pmd_bad(*pmd))
goto out;
printk(KERN_NOTICE "Valid pmd");
ptep = pte_offset_map(pmd, addr);
if (!ptep)
goto out;
addr = ptep->pte;
printk(KERN_INFO "byte = %d\n", *(char *)__va(addr));
pte_unmap(ptep);
If I understand correctly, addr should be the physical address corresponding to the user-space virtual address. Then I should be able to dereference that using __va. However, it doesn't work. If I use pte_page and kmap, though, it works exactly how it is supposed to. Why does this happen? I'm on x86-64, so high memory shouldn't be a problem? Is there something else kmap does?
I fixed the problem thanks to TonyTannous. The page table entry doesn't contain just the physical address, but also some other bits used for access rights and the like. The physical address can be obtained by masking it with PTE_PFN_MASK:
addr = ptep->pte & PTE_PFN_MASK
I can then dereference it with __va.
I'm using this code http://fivelinesofcode.blogspot.com/2014/03/how-to-translate-virtual-to-physical.html to dump the pfn related to a given virtual address taken from /proc/"pid"/maps.
Once I get the PFN, I dump it with a specific kernel module. This is a snippet of the code:
static int write_pfn(phys_addr_t pfn)
{
struct page *p;
void *v;
int s =0,ret =0;
p = pfn_to_page((pfn) >> PAGE_SHIFT);
v = kmap(p);
DBG("Writing page %d(mapped addr=0x%lx) - pfn: 0x%lx", p,v,pfn);
s = write_vaddr(v, PAGE_SIZE);
if (s != PAGE_SIZE) {
DBG("Error sending page %d(addr=0x%lx)", s,v);
return (int) s;
ret-=1;
}
kunmap(p);
return ret;
}
However, I have noticed that if I compare the PFN dumped with the kernel module with the real content of the corresponding virtual address inside the process , than the content is completely different.
Note that I dump the content of the process virtual address using the command "x" from (gdb).
Any idea? This is my kernel version:
Linux 3.14.7-rt5 #1 SMP Mon Jun 23 14:55:19 CEST 2014 x86_64 GNU/Linux
Logically your solution is correct, but i'am not sure about correctness of the pagemap reader code. Anyway, there is a way to check it. While you are inside kernel (in the context of target application) you could get page corresponding to specified VA using this kernel API:
1. mm = current->mm
2. struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
3. static inline struct page *follow_page(struct vm_area_struct *vma,
unsigned long address, unsigned int foll_flags)
4. page_to_pfn
You can implement this and compare with your results, Hope this will help.
Thank you Alex.
The problem with your solution is that some symbols are not exported (e.g. follow_page()) therefore I can't use them in my module.
However, I found this solution:
mm = ts->mm;
pgd_t * pgd = pgd_offset(mm, vaddr);
pud_t * pud = pud_offset(pgd, vaddr);
pmd_t * pmd = pmd_offset(pud, vaddr);
pte_t * pte = pte_offset_map(pmd, vaddr);
p = pte_page(*pte);
if(p)
DBG("page frame struct is # %p", p);
v = kmap(p);
DBG("Writing page 0x%lx(mapped addr=0x%lx) - pid: %d", v,vaddr,pidnr);
s = write_vaddr(v, PAGE_SIZE);
if (s != PAGE_SIZE) {
DBG("Error sending page %d(addr=0x%lx pid=%d)", v,vaddr,pidnr);
ret-=1;
}
kunmap(p);
pte_unmap(pte);