news 2026/5/15 21:31:19

qemu跑通linux

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
qemu跑通linux

1, 使用buildroot编译基于qemu_arm_vexpress_defconfig的linux镜像
a) 环境准备
确保你已经在 Linux 系统中安装了必要依赖:
bash
sudo apt update
sudo apt install git build-essential qemu-system-arm
libncurses-dev flex bison unzip python3

b) 第一步:获取并切换 Buildroot
git clone https://github.com/buildroot/buildroot.git
cd buildroot
git checkout 2024.02 # 推荐使用稳定版
c) 第二步:配置为 qemu_arm_vexpress_defconfig
make qemu_arm_vexpress_defconfig
这个配置会为:
QEMU 板子:vexpress-a9(Cortex-A9,ARMv7)
内核:Linux 6.x 系列
根文件系统:initramfs(默认内嵌)
d) 第三步:开始构建
make -j8
这个过程会:
下载交叉工具链
构建 Linux 内核
构建 busybox 和根文件系统
输出可直接运行的 QEMU 镜像
第一次构建可能需要 30 分钟以上,取决于网络和 CPU。
如果编译过程中报LD_LIBRARY_PATH相关的错误,执行
unset LD_LIBRARY_PATH
或许就解决问题了
e) 第四步:使用 QEMU 启动系统
构建成功后,使用以下命令启动系统:
./qemu-system-arm -M vexpress-a9 -m 256M -nographic -kernel zImage -dtb vexpress-v2p-ca9.dtb -append “root=/dev/mmcblk0 rootwait console=ttyAMA0” -drive file=rootfs.ext2,if=sd,format=raw
成功后你会看到:
Buildroot 2024.02 login:
登录名是 root,无密码。

2, 在linux上添加一个uart测试程序,用来控制一个外接串口,并实现数据的收发
a) 增加串口驱动,在/home/feng.xian/work/qemu/buildroot/buildroot/output/build/linux-6.1.44/drivers/tty/serial/目录下增加my-uart.c文件
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

// 添加模拟寄存器
struct my_uart_regs {
char thr; // Transmit Holding Register
char rbr; // Receiver Buffer Register
char lsr; // Line Status Register (bit 0: data ready, bit 5: transmit empty)
};

struct my_uart_dev {
void __iomem *base; // 依然保留用于后面扩展
struct miscdevice miscdev;
struct my_uart_regs regs;
struct mutex lock;
};

static ssize_t my_uart_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct my_uart_dev *dev = container_of(file->private_data, struct my_uart_dev, miscdev);
char ch;

if (count == 0) return 0; mutex_lock(&dev->lock); if (!(dev->regs.lsr & 0x01)) { mutex_unlock(&dev->lock); return 0; // 没有数据 } ch = dev->regs.rbr; pr_info("my_uart: RX char: %c\n", ch); dev->regs.lsr &= ~0x01; // 清除 data ready mutex_unlock(&dev->lock); if (copy_to_user(buf, &ch, 1)) return -EFAULT; return 1;

}

static ssize_t my_uart_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct my_uart_dev *dev = container_of(file->private_data, struct my_uart_dev, miscdev);
char kbuf[64];

if (count > sizeof(kbuf) - 1) count = sizeof(kbuf) - 1; if (copy_from_user(kbuf, buf, count)) return -EFAULT; kbuf[count] = '\0'; mutex_lock(&dev->lock); for (size_t i = 0; i < count; i++) { dev->regs.thr = kbuf[i]; dev->regs.lsr |= (1 << 5); // THR empty pr_info("my_uart: TX char: %c\n", kbuf[i]); dev->regs.rbr = kbuf[i]+1; dev->regs.lsr |= 0x01; // 数据准备好 } mutex_unlock(&dev->lock); return count;

}

static int my_uart_open(struct inode *inode, struct file *file)
{
struct my_uart_dev *dev = container_of(file->private_data, struct my_uart_dev, miscdev);
mutex_lock(&dev->lock);
memset(&dev->regs, 0, sizeof(dev->regs));
mutex_unlock(&dev->lock);
pr_info(“my_uart: open\n”);
return 0;
}

static const struct file_operations my_uart_fops = {
.owner = THIS_MODULE,
.read = my_uart_read,
.write = my_uart_write,
.open = my_uart_open,
};

static int my_uart_probe(struct platform_device *pdev)
{
struct resource *res;
struct my_uart_dev *dev;
int ret;

dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); // 注册字符设备 dev->miscdev.minor = MISC_DYNAMIC_MINOR; dev->miscdev.name = "myuart0"; dev->miscdev.fops = &my_uart_fops; ret = misc_register(&dev->miscdev); if (ret) return ret; platform_set_drvdata(pdev, dev); dev_info(&pdev->dev, "My UART probed at %p and registered as /dev/myuart0\n", dev->base); return 0;

}

static int my_uart_remove(struct platform_device *pdev)
{
struct my_uart_dev *dev = platform_get_drvdata(pdev);
misc_deregister(&dev->miscdev);
dev_info(&pdev->dev, “My UART removed\n”);
return 0;
}

static const struct of_device_id my_uart_of_match[] = {
{ .compatible = “mycompany,my-uart”, },
{},
};
MODULE_DEVICE_TABLE(of, my_uart_of_match);

static struct platform_driver my_uart_driver = {
.probe = my_uart_probe,
.remove = my_uart_remove,
.driver = {
.name = “my_uart”,
.of_match_table = my_uart_of_match,
},
};
module_platform_driver(my_uart_driver);

MODULE_LICENSE(“GPL”);
MODULE_DESCRIPTION(“Simple Custom UART Driver with Char Device”);
MODULE_AUTHOR(“fxian”);

b) 增加设备树中的描述
/home/feng.xian/work/qemu/buildroot/buildroot/output/build/linux-6.1.44/arch/arm/boot/dts/vexpress-v2m.dtsi 文件中增加如下内容:
v2m_serial3: uart@c000 {
compatible = “arm,pl011”, “arm,primecell”;
reg = <0x0c000 0x1000>;
interrupts = <8>;
clocks = <&v2m_oscclk2>, <&smbclk>;
clock-names = “uartclk”, “apb_pclk”;
};
// 以下为新增内容
my_uart: uart@d000 {
compatible = “mycompany,my-uart”;
reg = <0x0d000 0x1000>;
interrupts = <45>;
clocks = <&v2m_oscclk2>, <&smbclk>;
clock-names = “uartclk”, “apb_pclk”;
status = “okay”;
};

c) 在build根目录下执行编译命令 make linux-rebuild
d) 添加测试程序:
添加文件夹/home/feng.xian/work/qemu/buildroot/buildroot/package/myuart-test/
然后增加文件test.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define DEVICE_PATH “/dev/myuart0”

int main()
{
int fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror(“Failed to open device”);
return 1;
}

char write_buf = 'A'; if (write(fd, &write_buf, 1) < 0) { perror("Failed to write to device"); close(fd); return 1; } char read_buf; if (read(fd, &read_buf, 1) < 0) { perror("Failed to read from device"); close(fd); return 1; } printf("Read back from device: '%c'\n", read_buf); close(fd); return 0;

}
添加文件myuart-test.mk
################################################################################

myuart_test.mk - Makefile fragment for Buildroot

################################################################################

MYUART_TEST_VERSION = 1.0
MYUART_TEST_SITE = /home/feng.xian/work/qemu/buildroot/buildroot/package/myuart-test
MYUART_TEST_SITE_METHOD = local
MYUART_TEST_INSTALL_TARGET = YES

define MYUART_TEST_BUILD_CMDS
$(MAKE) -C(@D)CROSSCOMPILE="(@D) CROSS_COMPILE="(@D)CROSSCOMPILE="(TARGET_CROSS)" CC=“$(TARGET_CC)” myuart-test
endef

define MYUART_TEST_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/myuart-test $(TARGET_DIR)/usr/bin/myuart-test
endef

$(eval $(generic-package))
添加Makefile

This Makefile is used by the .mk script above

myuart-test:
$(CC) -static -o myuart-test test.c
添加Config.in
config BR2_PACKAGE_MYUART_TEST
bool “myuart-test”
help
A simple test program for the custom UART driver
default y
在/home/feng.xian/work/qemu/buildroot/buildroot/package/Config.in 文件中增加
source “package/myuart-test/Config.in”

然后重新执行编译,在 /home/feng.xian/work/qemu/buildroot/buildroot/目录下执行 make –j8

然后重新启动qemu
./qemu-system-arm -M vexpress-a9 -m 256M -nographic -kernel zImage -dtb vexpress-v2p-ca9.dtb -append “root=/dev/mmcblk0 rootwait console=ttyAMA0” -drive file=rootfs.ext2,if=sd,format=raw -d guest_errors -D qemu.log
-d guest_errors -D qemu.log 参数是用来保存qemu的执行日志的
在usr/bin/目录下会有我们生成myuart-test binary,执行,就会发现可以实现自收发
3, 在qemu上增加一个串口设备,在linux上增加其驱动,并将测试程序改成读写此串口;
a) 在quemu的代码中增加,/home/fxian/work/arm_test/qemu/qemu-master/hw/char/my-uart.c
#include “qemu/osdep.h”
#include “hw/sysbus.h”
#include “hw/irq.h”
#include “hw/registerfields.h”
#include “qemu/log.h”
#include “qemu/module.h”
#include “qapi/error.h”
#include “qemu/main-loop.h”

#define TYPE_MY_UART “my-uart”
#define MY_UART(obj) OBJECT_CHECK(MyUartState, (obj), TYPE_MY_UART)

typedef struct MyUartState {
SysBusDevice parent_obj;

MemoryRegion iomem; qemu_irq irq; uint32_t data_reg; uint32_t status_reg;

} MyUartState;

static uint64_t my_uart_read(void *opaque, hwaddr addr, unsigned size)
{
MyUartState *s = opaque;

switch (addr) { case 0x00: return s->data_reg; case 0x04: return s->status_reg; default: return 0; }

}

static void my_uart_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
MyUartState *s = opaque;

switch (addr) { case 0x00: s->data_reg = value; qemu_log_mask(LOG_GUEST_ERROR, "UART WRITE: 0x%02x\n", (uint8_t)value); break; case 0x04: s->status_reg = value; break; }

}

static const MemoryRegionOps my_uart_ops = {
.read = my_uart_read,
.write = my_uart_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 4,
}
};

static void my_uart_init(Object *obj)
{
MyUartState *s = MY_UART(obj);
memory_region_init_io(&s->iomem, obj, &my_uart_ops, s, “my_uart”, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
}

static void my_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = “My Simple UART”;
}

static const TypeInfo my_uart_info = {
.name = TYPE_MY_UART,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MyUartState),
.instance_init = my_uart_init,
.class_init = my_uart_class_init,
};

static void my_uart_register_types(void)
{
type_register_static(&my_uart_info);
}

type_init(my_uart_register_types)

b) 然后在文件/home/fxian/work/arm_test/qemu/qemu-master/hw/arm/vexpress.c中,vexpress_common_init函数中增加如下代码
DeviceState *my_uart_dev = qdev_new(“my-uart”);
sysbus_realize_and_unref(SYS_BUS_DEVICE(my_uart_dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(my_uart_dev), 0, 0x1000d000);
sysbus_connect_irq(SYS_BUS_DEVICE(my_uart_dev), 0, pic[45]);

c) hw/char/meson.build 文件中增加如下内容
system_ss.add(files(‘my-uart.c’))

d) 重新编译qemu
mkdir build && cd build
…/configure --target-list=arm-softmmu
make –j8

e) 修改linux驱动为:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>

#define DEVICE_NAME “myuart0”
#define CLASS_NAME “myuartclass”
#define MYUART_REG_SIZE 0x1000

static void __iomem *myuart_base;
static int irq_num;
static int major;
static struct cdev myuart_cdev;
static struct class *myuart_class;

static int myuart_open(struct inode *inode, struct file *file)
{
pr_info(“myuart: device opened\n”);
return 0;
}

static int myuart_release(struct inode *inode, struct file *file)
{
pr_info(“myuart: device closed\n”);
return 0;
}

static ssize_t myuart_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char val = readb(myuart_base); // 简化:从寄存器读一个字节
if (copy_to_user(buf, &val, 1))
return -EFAULT;
return 1;
}

static ssize_t myuart_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
char val;
if (copy_from_user(&val, buf, 1))
return -EFAULT;
writeb(val, myuart_base); // 简化:写一个字节
return 1;
}

static struct file_operations fops = {
.owner = THIS_MODULE,
.open = myuart_open,
.release = myuart_release,
.read = myuart_read,
.write = myuart_write,
};

static int myuart_probe(struct platform_device *pdev)
{
pr_info(“myuart_probe enter\n”);
struct resource *res;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0); myuart_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(myuart_base)) return PTR_ERR(myuart_base); pr_info("myuart base: %p\n", myuart_base); // irq_num = platform_get_irq(pdev, 45); // if (irq_num < 0) // return irq_num; // pr_info("myuart irq_num %d\n", irq_num); major = register_chrdev(0, DEVICE_NAME, &fops); if (major < 0) return major; myuart_class = class_create(THIS_MODULE, CLASS_NAME); device_create(myuart_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); cdev_init(&myuart_cdev, &fops); cdev_add(&myuart_cdev, MKDEV(major, 0), 1); dev_info(&pdev->dev, "myuart probed at %p\n", myuart_base); return 0;

}

static int myuart_remove(struct platform_device *pdev)
{
device_destroy(myuart_class, MKDEV(major, 0));
class_destroy(myuart_class);
unregister_chrdev(major, DEVICE_NAME);
cdev_del(&myuart_cdev);
return 0;
}

static const struct of_device_id myuart_of_match[] = {
{ .compatible = “mycompany,my-uart” },
{},
};
MODULE_DEVICE_TABLE(of, myuart_of_match);

static struct platform_driver myuart_driver = {
.driver = {
.name = “myuart”,
.of_match_table = myuart_of_match,
},
.probe = myuart_probe,
.remove = myuart_remove,
};

module_platform_driver(myuart_driver);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“fxian”);
MODULE_DESCRIPTION(“my test uart driver”);

重新编译linux,然后用新的qemu-arm-system 运行linux,就可以实现数据的自收发了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/13 15:11:18

使用 Node.js 轻松将你的应用后端接入 Taotoken 多模型服务

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用 Node.js 轻松将你的应用后端接入 Taotoken 多模型服务 将大模型能力集成到你的 Node.js 后端应用中&#xff0c;通常意味着需…

作者头像 李华
网站建设 2026/5/13 15:11:05

半导体制造合作博弈:从AMD代工决策看芯片供应链风险管理

1. 从一则旧闻说起&#xff1a;半导体产业的“蝴蝶效应”2011年底&#xff0c;EE Times上的一则报道在半导体圈内激起了不小的涟漪。报道援引内部消息称&#xff0c;AMD正酝酿对其产品路线图进行重大调整&#xff0c;核心焦点是其28纳米APU的制造可能从格罗方德转向台积电。当时…

作者头像 李华
网站建设 2026/5/13 15:10:22

终极免费SWF反编译工具:JPEXS Free Flash Decompiler完整指南

终极免费SWF反编译工具&#xff1a;JPEXS Free Flash Decompiler完整指南 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler JPEXS Free Flash Decompiler是一款强大且完全免费的开源Flash…

作者头像 李华
网站建设 2026/5/13 15:02:04

大数据hive_mr压缩问题

Hive中压缩的设置&#xff1a;注意 本质还是指的是MapReduce的压缩 –设置Hive的中间压缩 也就是map的输出压缩 1&#xff09;开启 hive 中间传输数据压缩功能 set hive.exec.compress.intermediatetrue; 2&#xff09;开启 mapreduce 中 map 输出压缩功能 set mapreduce.map.o…

作者头像 李华
网站建设 2026/5/13 15:01:22

IDM的配置

1 主界面2 选项3 常规-编辑4 添加文件类型5 总结按照如上操作, 浏览器查看视频时会有下载弹窗, 可以直接下载视频到本地。

作者头像 李华
网站建设 2026/5/13 14:59:53

HiveWE:现代魔兽争霸III地图编辑器终极指南

HiveWE&#xff1a;现代魔兽争霸III地图编辑器终极指南 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 还在为魔兽争霸III原版地图编辑器的缓慢加载和复杂操作而烦恼吗&#xff1f;HiveWE作为一款专注于速度…

作者头像 李华